이 글은 정수원 님의 Infrean 강의를 학습한 내용을 정리하여 작성합니다.
예외 처리 및 요청 캐시 필터: ExceptionTranslationFilter, RequestCacheAwareFilter
인증/인가 API - ExceptionTranslationFilter
AuthenticationException
- 인증 예외 처리
- AuthenticationEntryPoint 호출
- 로그인 페이지 이동, 401 오류 코드 전달 등
- 인증 예외가 발생하기 전의 요청 정보를 저장
- ReqeustCache - 사용자의 이전 요청 정보를 세션에 저장하고 이를 꺼내 오는 캐시 메카니즘
- SavedRequest - 사용자가 요청했던 request 파라미터값들, 그 당시의 헤더값들 등이 저장
- ex) 사용자가 특정 자원에 접근하고자 하는데 인증 예외가 발생해 로그인 페이지로 이동하는 경우 이동하고자 한 자원 정보를 저장한다.
- 인증이 완료되면 캐싱된 자원 정보를 토대로 이동시킨다.
- ReqeustCache - 사용자의 이전 요청 정보를 세션에 저장하고 이를 꺼내 오는 캐시 메카니즘
- AuthenticationEntryPoint 호출
AccessDeniedException
- 인가 예외 처리
- AccessDeniedHandler에서 예외 처리하도록 제공
- 이러한 예외를 발생시키는 필터는 FilterSecurityInterceptor라는 필터이며 스프링 시큐리티가 관리하는 보안 필터 중 마지막에 위치한다.
- FilterSecurityInterceptor 필터 바로 앞에 위치한 필터가 ExceptionTranslationFilter이다.
- ExceptionTranslationFilter는 FilterSecurityInterceptor에게
- FilterSecurityInterceptor는
- 사용자는 인증을 받지 않은 상태로 /user 자원에 접근하고자 한다.
- FilterSecurityInterceptor는 사용자가 인증받지 않은 사용자임을 판단하고 인증 예외를 발생시킨다.
- 정확히는 현재 /user 자원에 접근하고자 하는 사용자는 익명 사용자이므로 인가 예외가 발생할 것이다.
- 인가 예외가 발생하더라도 AccessDeniedException는 AccessDeniedHandler를 호출하지 않을 것이다.
- 익명 사용자 혹은 rememberMe를 통해 인증받은 사용자는 AccessDeniedHandler가 아닌 AuthenticationException 과정으로 이동한다.
- AuthenticationEntryPoint를 호출해 로그인 페이지로 이동시킨다.
- 그전에 SecurityContext 안의 인증 객체를 null로 만드는 작업을 수행한다.
- 또한 사용자의 요청 관련 정보 (/user)를 저장한다.
- DefaultSavedRequest 객체는 Session에 저장되며 해당 세션은 HttpSessionRequestCache가 관리한다.
- 인증을 받아 USER 권한을 가진 사용자가 ADMIN 권한을 가진 사용자만 접근 가능한 /user 자원에 접근하는 경우 인가 예외가 발생한다.
- ExceptionTranslationFilter는 인가 예외를 발생시킨다.
- AccessDeniedHandler를 호출해 후속 작업을 처리한다.
정리
- ExceptionTranslationFilter는 인증과 인가 예외를 처리하는 필터이다.
- 이러한 예외를 던져주는 필터는 FilterSecurityInterceptor (인가 처리를 담당하는 필터)이다.
http.exceptionHandling()
http.exceptionHandling() // 예외처리 기능이 작동한다. (ExceptionTranslationFilter가 동작한다.)
실습
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests((authz) -> authz
.antMatchers("/login").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/admin/pay").hasRole("ADMIN")
.antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')")
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
http.formLogin()
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
RequestCache requestCache = new HttpSessionRequestCache();
SavedRequest savedRequest = requestCache.getRequest(request, response);
String redirectUrl = savedRequest.getRedirectUrl();
response.sendRedirect(redirectUrl);
}
});
http
.exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendRedirect("/login");
}
}) // 인증 예외 처리
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect("/denied");
}
}); // 인가 예외 처리
return http.build();
}
}
- AuthenticationEntryPoint와 AccessDeniedHandler 인터페이스를 직접 구현한다.
- 인증 예외가 발생한 경우 /login 페이지로 이동한다.
- 인가 예외가 발생한 경우 /denied 페이지로 이동한다.
- 인증 예외가 발생하는 경우 인증 예외가 발생하기 전의 요청 정보를 미리 저장한다 했다.
- formLogin() 메서드 이후에 successHandler() 메서드를 통해 해당 정보를 가져와 리다이렉트 한다.
- /login 페이지의 경우 권한이 없더라도 접근이 가능해야 하므로 permitAll() 메서드를 사용해 모든 사용자가 접근 가능하도록 한다.
- /denied 페이지의 경우 이미 인증을 받고 권한을 가진 사용자이므로 permitAll() 메서드를 적용하지 않는다.
주의!
AutehnticationEntryPoint 인터페이스를 직접 구현했으므로 스프링 시큐리티가 기본으로 제공해 주는 /login 페이지가 아닌 우리가 직접 생성한 /login 페이지로 이동한다.
별도로 /login 자원에 대한 컨트롤러를 생성해야 한다.
실행
- 다음과 같이 로그인 페이지로 이동한다.
- authenticationEntryPoint() 메서드 주석 처리 후 successHandler가 정상적으로 동작하는지 확인한다.
- 인증을 받지 않은 사용자가 루트 (/) 페이지로 이동하고자 하는 경우 로그인 페이지로 리다이렉트 된다.
- 인증을 수행하면 세션에 저장되어 있던 사용자의 이전 요청 정보를 토대로 리다이렉트 시킨다.
- 이 경우 사용자는 루트 페이지로 이동할 것이다.
- 권한이 없는 사용자가 /admin 자원으로 접근하는 경우 인가 예외가 발생하며 사용자는 /denied 페이지로 이동한다.
'스프링 시큐리티 > 스프링 시큐리티 기본 API 및 Filter 이해' 카테고리의 다른 글
사이트 간 요청 위조 - CSRF, CsrfFilter (0) | 2023.01.28 |
---|---|
권한설정과 표현 (0) | 2023.01.27 |
세션 제어 필터: SessionManagementFilter, ConcurrentSessionFilter (0) | 2023.01.11 |
동시 세션 제어, 세션 고정 보호, 세션 정책 (0) | 2023.01.11 |
익명 사용자 인증 필터: AnonymousAuthenticationFilter (0) | 2023.01.11 |