ExceptionTranslationFilter
크게 2가지의 Exception 을 처리한다.
-
AuthenticationException : 인증 예외
-
AccessDeniedException : 인가 예외
AuthenticationException , AccessDeniedException은 누가 Throw 할까.
FilterSecurityInterceptor 라는 필터이다.
-
SpringSecurity의 보안필터 중 제일 마지막에 위치한다.
-
FilterSecurityInterceptor 앞에 위치한 Filter 가 ExceptionTranslationFilter 이다.
-
ExceptionTranslationFilter 는 Try-Catch 문으로 감싸서 FilterSecurityInterceptor 를 호출한다.
-
FilterSecurityInterceptor 에서 발생한 AuthenticationException , AccessDeniedException 은 자신을 호출한 ExceptionTranslationFilter 에게 해당 Exception을 Throw 한다.
ExceptionTranslationFiler 의 AuthenticationException 처리
-
SpringSecurity 는 AuthenticationEntryPoint 인터페이스의 구현체를 제공
-
로그인 페이지 이동 , 401( Unauthorized Error : 인증 자격 증명이 없음 ) 상태 코드 전달
-
AuthenticationEntryPoint 를 직접 구현하면 원하는 후속처리를 할 수 있다.
2. 인증 예외가 발생하기 전 요청 정보를 저장
-
RequestCache : 사용자가 요청한 URL 정보 등을 가진 SavedRequest 객체를 Session에 저장하고 꺼내는 캐시 메커니즘을 가졌다. 로그인 페이지 에서 인증 성공 시 바로 요청했던 URL 로 이동시킨다.
-
SavedRequest : 사용자가 요청했던 RequestParameter , RequestHeader 를 저장, 실질적인 유저의 요청정보 를 가진다.
ExceptionTranslationFilter 의 AccessDeniedException 처리
-
AccessDeniedHandler 에서 예외 처리를 하도록 함
-
AccessDeniedHandler 인터페이스를 구현한 구현체를 등록하면 메시지로 어떤 권한 필요한지의 후속처리를 할 수 있다.
ExceptionTranslationFilter 의 AuthenticationException 구체적 처리 방식
-
유저가 인증받지않고 특정 자원으로 Request
-
FilterSecurityInterceptor 가 AccessDeniedException 발생 ( 익명사용자 이므로 AnonymousAuthenticationFilter가 AnonymousAuthenticationToken 을 만든 상태)
-
ExcetionTranslationFilter 가 AccessDeniedException 을 Catch하지만, 익명사용자거나 (AnonymousAuthenticationFilter 의 AnonymousAuthenticationToken) , RememberMe 기능(RememberMeAuthenticationFilter 의 RememberMeServices가 만든 RememberMeAuthenticationToken)을 가지고 있을 경우 AuthenticationException 처리 과정으로 넘긴다.
-
SecurityContext의 Authentication 객체를 null로 처리
-
AuthenticationEntryPoint의 구현체를 호출, AuthenticationEntryPoint가 로그인 페이지로 Redirect , 구현체가 있다면 후속 작업.
-
RequestCache 의 구현체인 HttpSessionRequestCache 가 사용자의 Request 정보를 SavedRequest의 구현체인 DefaultSavedRequest에 저장, HttpSessionRequestCache를 다시 Session에 저장한다.
ExceptionTranslationFilter 의 AccessDeniedException 구체적 처리방식
-
유저가 인증을 한 상태에서 ROLE 에 맞지않는 자원으로의 Request
-
FilterSecurityInterceptor 가 AccessDeniedException 을 발생
-
ExceptionTranslationFilter 가 AccessDeniedException을 Catch, AccessDeniedHandler 호출
-
AccessDeniedHandler 가 /denied 페이지로 Redirect, AccessDeniedHandler 구현체가 있다면 후속 작업.
예외 처리를 위한 API들
http.exceptionHandling() : 예외처리 기능이 작동
http.exceptionHandling() 의 하위 API들
-
authenticationEntryPoint(authenticationEntryPoint()) : 인증 실패 처리 구현객체, AuthenticationEntryPoint 구현객체가 온다.
-
accessDeniedHandler(accessDeniedHanlder()) : 인가 실패 처리 구현객체, AccessDeniedHandler 구현 객체가 온다.
** 코드를 자세히보면, formLogin() 의 로그인 기능 활성화 하위 API 중 successHanlder() 메서드에 AuthenticationSuccessHandler 구현체가 온 것을 볼 수 있는데, 여기서 RequestCache 의 구현체인 HttpSessionRequestCache 객체를 생성하여 SavedRequest 객체를 가져오고, SavedRequest 객체에서 getRedirectIrl() 로 사용자가 요청했던 url 로 Redirect 시키는 것을 볼 수 있다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("{noop}1111").roles("USER");
auth.inMemoryAuthentication().withUser("sys").password("{noop}1111").roles("USER","ADMIN");
auth.inMemoryAuthentication().withUser("admin").password("{noop}1111").roles("USER","SYS","ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()//인가 요청들
.antMatchers("/login").permitAll()//로그인 페이지는 모든 접근 허가
.antMatchers("/user").hasRole("USER")//해당 경로에 대해 USER 권한 심사를 하겠다.
.antMatchers("/admin/pay").hasRole("ADMIN")// 해당 경로에 대해 ADMIN 권한 심사를 하겠다.
.antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')")// 해당 경로에 대해 SpEL 이 true 인지 권한 심사를 하겠다.
.anyRequest()//어떤 요청이든
.authenticated();//인증 성공시 접근가능하다.
http
.formLogin()
.successHandler(new AuthenticationSuccessHandler() {//사용자의 인증 시 사용자가 요청했던 경로로 보내주는 구현
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
RequestCache requestCache = new HttpSessionRequestCache();//SavedRequest 저장한 RequestCache 객체 생성.
SavedRequest savedRequest = requestCache.getRequest(request, response);//HttpSessionRequestCache 에서 유저 요청정보를 저장한 SavedRequest 객체 생성.
String redirectUrl = savedRequest.getRedirectUrl(); // SavedRequest 에서 사용자가 가고자하는 경로 추출
response.sendRedirect(redirectUrl);// 가고자 했던 경로로 Redirect
}
});//폼 로그인 방식을 사용하겠다.
http
.exceptionHandling()// 인증, 인가 예외처리 기능 활성화
.authenticationEntryPoint(new AuthenticationEntryPoint() {//AuthenticationException 발생시 처리
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
System.out.println("인증 예외 발생 "+authException.getMessage());
response.setStatus(401);
response.sendRedirect("/login");
}
})
.accessDeniedHandler(new AccessDeniedHandler() {//AccessDeniedException 발생시 처리
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println("인가 예외 발생 "+accessDeniedException.getMessage());
response.sendRedirect("/denied");
}
});
}
}
RequestCacheAwareFilter
-
유저의 요청정보를 가진 SavedRequest를 RequestCache 에 저장하고 RequestCache 를 Session 담음
-
RequestCacheAwareFilter는 SavedRequest 존재여부를 확인하고 null 이 아닐경우 다음 Filter로 넘겨주는 역할을 한다.
-
즉, 다른 Filter에서도 SavedRequest의 정보를 활용할 수 있도록 해준다.
'springframework > 시작하자SpringSecurity' 카테고리의 다른 글
14.DelegatingFilterProxy, FilterChainProxy (0) | 2020.09.21 |
---|---|
13. 사이트 간 요청위조 : CSRF, CsrfFilter (0) | 2020.09.21 |
11. 권한 설정과 표현식 (0) | 2020.09.21 |
10.SessionManagementFilter, ConcurrentSessionFilter (0) | 2020.09.21 |
9.세션 동시 제어 / 세션 고정 보호 / 세션 정책 (0) | 2020.09.21 |