인가 예외처리는 ExceptionTranslationFilter가 처리한다는 것을 기억해야한다.
ExceptionTranslationFilter를 작동시키는 API가 exceptionHandling() 이다.
예외를 던지는 주체는 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 가 작동하도록 초기화) 의 하위 API 중 accessDeniedHandler() 에 빈으로 등록한 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";
}
'springframework > 시작하자SpringSecurity' 카테고리의 다른 글
35.AjaxAuthenticationFilter (0) | 2020.09.29 |
---|---|
34. Ajax Authentication Flow (0) | 2020.09.29 |
32.CustomAuthenticationFailureHanlder (0) | 2020.09.29 |
31.CustomAuthenticationSuccessHandler (0) | 2020.09.29 |
30.WebAuthenticationDetails, AuthenticationDetailsSource (0) | 2020.09.29 |