Spring-security源码-Filter之ExceptionTranslationFilter(十八)

统一的异常处理过滤器,我们可以处理统一的身份认证或者授权的异常 比如未登录访问登录页面 需要认证权限的地方

通过HttpSecurity  可以指定 

通过org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer

private void applyDefaultConfiguration(HttpSecurity http) throws Exception {
        //http本质也是build 这里都是配置默认的config configure add CsrfConfigurer
        http.csrf();
        //默认增加一个WebAsyncManagerIntegrationFilter
        http.addFilter(new WebAsyncManagerIntegrationFilter());
        //configures add ExceptionHandlingConfigurer
        http.exceptionHandling();
        //configures add HeadersConfigurer
        http.headers();
        //configures add SessionManagementConfigurer
        http.sessionManagement();
        //configure add SecurityContextConfigurer
        http.securityContext();
        //configure add RequestCacheConfigurer
        http.requestCache();
        ///configure add AnonymousConfigurer
        http.anonymous();
        ///configure add ServletApiConfigurer
        http.servletApi();
        //configure DefaultLoginPageConfigurer
        http.apply(new DefaultLoginPageConfigurer<>());
        //configure LogoutConfigurer
        http.logout();
    }

org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer#configure

 @Override
    public void configure(H http) {
        //身份验证入口点(驱动应用开始进行身份验证),用于启动身份验证方案(默认:Http403ForbiddenEntryPoint)
        AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
        ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,
                getRequestCache(http));
        //获得处理异常的handle
        AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
        exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
        //autowired注入
        exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
        //加入httpConfig
        http.addFilter(exceptionTranslationFilter);
    }

org.springframework.security.web.access.ExceptionTranslationFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)

 private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        //tryCath捕获异常
        try {
            chain.doFilter(request, response);
        }
        catch (IOException ex) {
            throw ex;
        }
        catch (Exception ex) {

            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);
            }
            //<1>处理异常
            handleSpringSecurityException(request, response, chain, securityException);
        }
    }

<1>

org.springframework.security.web.access.ExceptionTranslationFilter#handleSpringSecurityException

 private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
                                               FilterChain chain, RuntimeException exception) throws IOException, ServletException {
        //授权类异常
        if (exception instanceof AuthenticationException) {
            //<2>
            handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
        }
        //身份认证异常
        else if (exception instanceof AccessDeniedException) {
           // <4>
            handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);
        }
    }

<2>

 private void handleAuthenticationException(HttpServletRequest request, HttpServletResponse response,
                                               FilterChain chain, AuthenticationException exception) throws ServletException, IOException {
        //<3>
        sendStartAuthentication(request, response, chain, exception);
    }

<3>

 

 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);
        //保存当前Request的请求信息,例如:header、cookie、path等,以便在用户进行身份验证后可以检索和重用该请求
        requestCache.saveRequest(request, response);
        logger.debug("Calling Authentication entry point.");
        //开始启动验证(例如通过重定向到登录界面)
        authenticationEntryPoint.commence(request, response, reason);
    }

<4>

 private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,
                                             FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        /**
         * 判断是是否是记住登录或者是未登录authenticationTrustResolver
         * authenticationTrustResolver主要是判断authentication是否是AnonymousAuthenticationToken
         * 或者RememberMeAuthenticationToken 类型
         */
        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);
            }
            //交由accessDeniedHandler 处理
            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);
        //最终交给authenticationEntryPoint处理
        this.authenticationEntryPoint.commence(request, response, reason);
    }

 

posted @ 2021-11-12 19:16  意犹未尽  阅读(454)  评论(0编辑  收藏  举报