모르지 않다는 것은 아는것과 다르다.

Spring Security

AuthenticationSuccessHandler, AuthenticationFailureHandler, AccessDeniedHandler 커스텀하기

채마스 2022. 2. 28. 20:39



개요

  • 폼 인증, 인가를 성공하거나 실패한 후에 처리하고 싶은 작업을 AuthenticationSuccessHandler, AuthenticationFailureHandler, AccessDeniedHandler 를 커스텀해서 구현할 수 있다.
  • AuthenticationSuccessHandler 는 인증 성공 후 처리
  • AuthenticationFailureHandler 는 인증 실패 후 처리
  • AccessDeniedHandler 는 인가 실패 후 처리





AuthenticationSuccessHandler 커스텀

  • CustomAuthenticationSuccessHandler 클래스 구현
@Component
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        setDefaultTargetUrl("/");

        SavedRequest saveRequest = requestCache.getRequest(request, response);
        if(saveRequest != null){
            String targetUrl = saveRequest.getRedirectUrl();
            redirectStrategy.sendRedirect(request, response, targetUrl);
        }else{
            redirectStrategy.sendRedirect(request, response, getDefaultTargetUrl());
        }
    }
}
  • SimpleUrlAuthenticationSuccessHandler 클래스를 상속 받고 onAuthenticationSuccess 메소드를 재정의 해준다.
  • 만약 애초에 인증 페이지로 접속한 거라면, saveRequest 가 null 이기 때문에 루트 페이지로 보내고
  • 만약 다른 페이지를 접속했다가 권한이 없어서 인증 페이지로 접속한 거라면, saveRequest 에 이전 요청 정보가 담기고, 인증처리가 끝난 후, 이전 요청 페이지로 이동시키는 코드이다.





AuthenticationFailureHandler 커스텀

  • CustomAuthenticationFailureHandler 클래스 구현
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        String errorMessage = "Invalid Username or Password";

        if(exception instanceof BadCredentialsException){
            errorMessage = "Invalid Username or Password";
        }else if(exception instanceof InsufficientAuthenticationException){
            errorMessage = "Invalid Secret Key";
        }

        setDefaultFailureUrl("/login?error=true&exception=" + errorMessage);

        super.onAuthenticationFailure(request, response, exception);

    }
}
  • SimpleUrlAuthenticationFailureHandler 클래스를 상속 받고 onAuthenticationFailure 메소드를 재정의 해준다.
  • AuthenticationProvider 에서 인증이 실패하면 그 결과를 인증필터가 받아서 CustomAuthenticationFailureHandler 호출하게 된다.
  • AuthenticationProvider 로 부터 받은 예외에 따라 에러메시지를 만들어주고, error, exception 을 requestParam 으로 담아서 로그인 페이지를 url 로 이동할 수 있도록 세팅한다.
  • 마지막으로 부모에게 요청에 대한 처리를 넘긴다.





AccessDeniedHandler 커스텀

  • CustomAccessDeniedHandler 클래스 구현
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    private String errorPage;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        String deniedUrl = errorPage + "?exception=" + accessDeniedException.getMessage();
        response.sendRedirect(deniedUrl);
    }

    public void setErrorPage(String errorPage) {
        this.errorPage = errorPage;
    }
}
  • 인가 예외는 ExceptionTranslationFilter 가 처리한다.
  • 그렇기 때문에 인가에 대한 예외도 따로 처리할 수 있는 핸들러가 필요하다. -> 그 기능을 CustomAccessDeniedHandler 가 처리해 줄 수 있다.
  • 위의 코드는 ExceptionTranslationFilter 로 부터 인가 예외가 발생하면 에러 메시지를 만들고 리다이렉트하는 코드이다.





SecurityConfig 에 CustomAuthenticationSuccessHandler,CustomAuthenticationFailureHandler, AccessDeniedHandler 등록

@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ... 생략
    private final AuthenticationSuccessHandler customAuthenticationSuccessHandler;
    private final AuthenticationFailureHandler customAuthenticationFailureHandler;

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
                // ... 생략
                .antMatchers("/login*").permitAll()
        .and()
                // ... 생략
                .successHandler(customAuthenticationSuccessHandler)
                .failureHandler(customAuthenticationFailureHandler)
                // ... 생략
        .and()
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler())
        ;

    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler();
        accessDeniedHandler.setErrorPage("/denied");
        return accessDeniedHandler;
    }
}
  • setDefaultFailureUrl("/login?error=true&exception=" + errorMessage); 를 처리하기 위해서 "/login*" 에 대한 리소스 접근을 허용한다.
  • customAuthenticationSuccessHandler, customAuthenticationFailureHandler, accessDeniedHandler 를 등록해 준다.




REFERENCES

  • 정수원님의 스프링 시큐리티