HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
🔐
Spring Security
/
🌇
RequestCacheAwareFilter
🌇

RequestCacheAwareFilter

  • 인증 요청에 의해 잠시 스탑된 요청을 로그인 후 원래 가려던 요청으로 이동하도록 도와주는 필터임
  • 익명 사용자가 보호 받는 리소스 (예: /me)에 접근할 경우
    • 접근 권한 확인을 위해 해당 요청을 FilterSecurityInterceptor로 넘김 → AccessDeicisionManager로 위임
    • 접근 권한이 없기 때문에 AccessDecisionManager 에서 접근 거부 예외가 발생함
    • ExceptionTranslationFilter 가 FilterSecurityInterceptor 에서 발생한 접근 거부 예외를 처리함
      • 현재 사용자가 익명 사용자라면, 보호 받는 리소스로의 접근을 캐시처리하고, 로그인 페이지로 이동 시킴
  1. 권한이 필요한 resource이면 FilterSecurityInterceptor의 doFilter 적용
  1. doFilter() → invoke() → filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
  1. ExceptionTranslationFilter의 doFilter 호출됨 → this.handleSpringSecurityException(request, response, chain, (RuntimeException)securityException);
  1. handleSpringSecurityException에서 handleAccessDeniedException으로 넘어감
  1. handleAccessDeniedException에서 login page로 redirect & requestCache(세션)에 request를 저장함 → 로그인 페이지로 리다이렉션
  1. (로그인) → RequestCacheAwareFilter의 doFilter에서 세션에 저장된 request를 확인하여 원래 가려던 곳으로 이동함
    1. private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException { if (exception instanceof AuthenticationException) { this.handleAuthenticationException(request, response, chain, (AuthenticationException)exception); } else if (exception instanceof AccessDeniedException) { this.handleAccessDeniedException(request, response, chain, (AccessDeniedException)exception); } } private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AccessDeniedException exception) throws ServletException, IOException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication); if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) { if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Sending %s to authentication entry point since access is denied", authentication), exception); } sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException( this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication", "Full authentication is required to access this resource"))); } else { if (logger.isTraceEnabled()) { logger.trace( LogMessage.format("Sending %s to access denied handler since access is denied", authentication), exception); } this.accessDeniedHandler.handle(request, response, exception); } } protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException { // SEC-112: Clear the SecurityContextHolder's Authentication, as the // existing Authentication is no longer considered valid SecurityContextHolder.getContext().setAuthentication(null); this.requestCache.saveRequest(request, response); this.authenticationEntryPoint.commence(request, response, reason); }
      ExceptionTranslationFilter 구현 일부 발췌
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest wrappedSavedRequest = this.requestCache.getMatchingRequest((HttpServletRequest)request, (HttpServletResponse)response); chain.doFilter((ServletRequest)(wrappedSavedRequest != null ? wrappedSavedRequest : request), response); }
      RequestCacheAwareFilter의 doFilter에서 session에 저장된 request를 가지고 옴