[SpringSecurity5.6.2源码分析二十三]:ExceptionTranslationFilter

前言

  • ExceptionTranslationFilter是SpringSecurity中负责处理异常的过滤器
  • 这里的异常指的是下面两大异常
    • AuthenticationException:认证异常
    • AccessDeniedException:访问被拒绝异常
  • 这里的异常是由SpringSecurity中负责进行权限校验的FilterSecurityInterceptor抛出的

1. ExceptionHandlingConfigurer

  • ExceptionHandlingConfigurer是ExceptionTranslationFilter对应的配置类
  • 也是默认开启的配置类之一
    image.png

1.1 常用方法

  • 配置类中的常用方法都是为了配置下面的对象
public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
      extends AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>, H> {

   private AuthenticationEntryPoint authenticationEntryPoint;

   private AccessDeniedHandler accessDeniedHandler;

   private LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> defaultEntryPointMappings = new LinkedHashMap<>();

   private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = new LinkedHashMap<>();
}
  • authenticationEntryPoint:身份验证入口点 是抛出认证异常才会执行的,比如说回到登录页的实现类LoginUrlAuthenticationEntryPoint
  • accessDeniedHandler:捕获了访问被拒绝异常的处理器
  • defaultEntryPointMappingsdefaultDeniedHandlerMappings:存放不同请求路径的的访问被拒绝的多个处理器 key:请求匹配器,比如说匹配/user value:对应的访问被拒绝的处理器

1.2 configure(…)

public void configure(H http) {
   //获得身份认证入口点
   AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
   //创建处理异常的过滤器,还传入了请求缓存器
   ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,
         getRequestCache(http));
   //获得访问被拒绝处理器
   AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
   exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
   //进行objectPostProcessor处理
   exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
   //添加过滤器到httpSecurity中
   http.addFilter(exceptionTranslationFilter);
}

2. ExceptionTranslationFilter

  • doFilter(…)方法的核心逻辑是try住下一个过滤器,而这个过滤器就是FilterSecurityInterceptor
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      throws IOException, ServletException {
   try {
      chain.doFilter(request, response);
   }
   catch (IOException ex) {
      throw ex;
   }
   catch (Exception ex) {
      //尝试从堆栈跟踪中提取SpringSecurityException,不懂
      Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
      //拿到认证异常
      RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
            .getFirstThrowableOfType(AuthenticationException.class, causeChain);
      //不是一个认证异常
      if (securityException == null) {
         securityException = (AccessDeniedException) this.throwableAnalyzer
               .getFirstThrowableOfType(AccessDeniedException.class, causeChain);
      }
      //既不是认证异常也不是访问被拒绝异常,那就继续抛
      if (securityException == null) {
         rethrow(ex);
      }
      if (response.isCommitted()) {
         throw new ServletException("Unable to handle the Spring Security Exception "
               + "because the response is already committed.", ex);
      }
      //处理SpringSecurity异常
      handleSpringSecurityException(request, response, chain, securityException);
   }
}
  • 而catch住的异常都会通过ThrowableAnalyzer转为认证异常和访问被拒绝异常,然后丢给handleSpringSecurityException(…)方法继续处理
  • 再看handleSpringSecurityException(…)方法,这里就分别对于两个异常有不同的处理逻辑了
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
      FilterChain chain, RuntimeException exception) throws IOException, ServletException {
   //是一个认证异常
   if (exception instanceof AuthenticationException) {
      handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
   }
   //是一个访问被拒绝异常
   else if (exception instanceof AccessDeniedException) {
      handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);
   }
}

2.1 handleAuthenticationException(…)

    private void handleAuthenticationException(HttpServletRequest request, HttpServletResponse response,
          FilterChain chain, AuthenticationException exception) throws ServletException, IOException {
       this.logger.trace("Sending to authentication entry point since authentication failed", exception);
       sendStartAuthentication(request, response, chain, exception);
    }

    /**
     * 处理认证异常
     */
    protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
          AuthenticationException reason) throws ServletException, IOException {
       //清除存储在线程级别的上下文策略的认证信息,HttpSession级别的会在SecurityContextPersistenceFilter的finally代码块中被更新
       //因为现有的认证不再被认为有效
       SecurityContext context = SecurityContextHolder.createEmptyContext();
       SecurityContextHolder.setContext(context);
       //将当前的请求放入请求缓存器
       //这样当重新登录后,还能将请求包装为这一次请求
       this.requestCache.saveRequest(request, response);
       //执行认证异常处理器
       this.authenticationEntryPoint.commence(request, response, reason);
    }

2.2 handleAccessDeniedException(…)

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);
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值