Spring Security

인증 필터는 어떻게 재정의할 수 있을까?

iksadnorth 2023. 9. 1. 20:24

👣 인증 필터의 업무 파악

우선 인증 필터가 수행해야 하는 업무를 나열하면 다음과 같다.

  1. 인증 전 AuthenticationToken을 만들기
  2. 해당 Authentication Token을 AuthenticationManager에게 위임해서 인증된 AuthenticationToken 만들기
  3. SecurityContextHolder에 안착시키기
  4. 인증 오류 발생 시, 오류 처리하기

위 업무 중 3번과 4번을 미리 해결한 추상 클래스를 찾아보면 
AbstractAuthenticationProcessingFilter 라는 클래스를 찾아볼 수 있다.

 

👣 AbstractAuthenticationProcessingFilter 분석

AbstractAuthenticationProcessingFilter는 
3번을 해결하기 위해 successfulAuthentication() 메서드를 미리 정의했고 재정의가 가능하도록 설계되어 있다.
4번을 해결하기 위해 unsuccessfulAuthentication() 메서드를 정의했고 역시 재정의가 가능하다.

AbstractAuthenticationProcessingFilter는 추상 메서드로 1번과 2번을 해결하도록 강제한다.
그리고 해당 추상 메서드는 attemptAuthentication() 라는 이름을 가지고 있다.

 

👣 AbstractAuthenticationProcessingFilter를 이용한 구현

다음은 내가 직접 구현한 인증 필터 코드다.

public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public CustomAuthenticationFilter(String urlWhenRunningAuthenticatingProcess, AuthenticationManager authenticationManager) {
        super(urlWhenRunningAuthenticatingProcess);
        super.setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        CustomAuthenticationToken unauthenticatedToken = obtainToken(request);

        return getAuthenticationManager().authenticate(unauthenticatedToken);
    }

    private CustomAuthenticationToken obtainToken(HttpServletRequest request) {
        String username = request.getHeader("username");
        String password = request.getHeader("password");

        return CustomAuthenticationToken.unauthenticated(username, password);
    }
}

위 코드를 보면 생성자가 거추장스럽게 인자를 2개나 받아야 한다.
첫 번째 인자는 어떤 로그인 URL에 대해서만 반등할 것이냐에 대한 파라미터고
두 번째 인자는 인증 과정을 직접 수행할 AuthenticationManager를 위한 파라미터다.

참고로 AuthenticationManager는 Bean으로 등록되는 것이 아니라서 다음과 같이 설정해줘야 한다.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    AuthenticationConfiguration authenticationConfiguration;

    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        return new CustomAuthenticationFilter("/api/v1/login", authenticationConfiguration.getAuthenticationManager());
    }
    
    ...

 

👣 결론

Filter를 상속해서 밑바닥부터 커스터마이징할 수도 있지만
AbstractAuthenticationProcessingFilter를 상속하면
단순히 인증 과정에만 관여할 수 있기 때문에 난이도가 급하강한다.