springframework/시작하자SpringSecurity

33.CustomAccessDeniedHandler

Jungsoomin :) 2020. 9. 29. 02:24

인가 예외처리ExceptionTranslationFilter가 처리한다는 것을 기억해야한다.

 

ExceptionTranslationFilter를 작동시키는 APIexceptionHandling() 이다.

 


 

예외를 던지는 주체는 FilterSecurityIntercepter

 

  • FilterChainProxy Filter Type Bean중에서 마지막에 위치한다.
  • AccessDecisionManager > AccessDecisionVotor 로 넘어가 심사를 한뒤 AccessDeniedExcpetion 이 던져진다.
  • 이를 받아 다시 ExceptionTranslationFilter에게 AccessDeniedException을 던진다.
  • ExceptionTranslationFilter AccessDeniedHandler 를 호출

인증과 인가 예외의 차이점

  • 인증 예외는 UsernameAuthenticationFilter가 하며
  • 인가 예외는 ExceptionTranslationFilter가 한다.

 


public void handle(request, response, accessDeniedException)

  • 사용자가 어떤 이유로 인가가 불가됬는지 메세지를 전달해줘야한다. SecurityContextHolder SecurityContext 안에 Authentication 객체에 인가 정보와 인증정보가 있다.
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;
    }
}

등록

등록은 SpringSecurity 설정클래스에서 한다.

exceptionHandling() < (ExceptionTranslationFilter 가 작동하도록 초기화) 의 하위 APIaccessDeniedHandler()빈으로 등록한 CustomAccessDeniedHandler를 넘긴다.

 

@Bean
    public AccessDeniedHandler customAccessDeniedHandler(){
        CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler();
        accessDeniedHandler.setErrorPage("/denied");
        return accessDeniedHandler;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/","/users","user/login/**","/login*").permitAll()
                .antMatchers("/mypage").hasRole("USER")
                .antMatchers("/messages").hasRole("MANAGER")
                .antMatchers("/config").hasRole("ADMIN")
                .anyRequest().authenticated()

            .and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .successHandler(customAuthenticationHandler)
                .failureHandler(customAuthenticationFailureHandler)
                .loginProcessingUrl("/login_proc")
                .permitAll()
        .and()
                .exceptionHandling()
                .accessDeniedHandler(customAccessDeniedHandler());
    }

컨트롤러 에서 받아내고 있다. 가만보면 SecurityContextHolder 에서 SecurityContext 를 가져와 Authentication을 꺼내 유저정보를 활용하고 있다.

@GetMapping("/denied")
    public String accessDenied(@RequestParam(value = "exception",required = false) String exception, Model model){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Account account = (Account)authentication.getPrincipal();
        model.addAttribute("username",account.getUsername());
        model.addAttribute("exception", exception);

        return "user/login/denied";
    }