spring security 4 filter ExceptionTranslationFilter(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18416057/article/details/88062156

今天学习的filter是ExceptionTranslationFilter,看名字是异常翻译过滤器

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;

        try {
        // 进入下一个filter
            chain.doFilter(request, response);
            this.logger.debug("Chain processed normally");
        } catch (IOException var9) {
            throw var9;
        } catch (Exception var10) {
         // 获取Thrrowable
            Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);

         // 判断异常是否是AuthenticationException
            RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
            if (ase == null) {
// 判断异常是否是AccessDeniedException
                ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
            }

            if (ase == null) {
                if (var10 instanceof ServletException) {
                    throw (ServletException)var10;
                }

                if (var10 instanceof RuntimeException) {
                    throw (RuntimeException)var10;
                }

                throw new RuntimeException(var10);
            }
       // 如果是AccessDeniedException 或者AuthenticationException则进入如下方法

            this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
        }

    }

   

 下面是handleSpringSecurityException方法的源码



    private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
        if (exception instanceof AuthenticationException) {
            this.logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
            this.sendStartAuthentication(request, response, chain, (AuthenticationException)exception);
        } else if (exception instanceof AccessDeniedException) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (!this.authenticationTrustResolver.isAnonymous(authentication) && !this.authenticationTrustResolver.isRememberMe(authentication)) {
                this.logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
                this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
            } else {
                this.logger.debug("Access is denied (user is " + (this.authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point", exception);
                this.sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
            }
        }

    }

  1.handleSpringSecurityException方法首先判断是否是AuthenticationException异常,如果是先用RequestCache保存前一步链接(到时候登录成功之后可以直接跳到用户操作的url),然后跳转到登录页面。

跳转到登录界面前,HttpSessionRequestCache会保存当前请求的url,登录认证通过后会直接跳转到之前访问的页面。使用这个功能不能将<form-login>的always-use-default-target属性设置为TRUE,默认是FALSE的。这个逻辑在SavedRequestAwareAuthenticationSuccessHandler中处理。

 protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
       // SecurityContextHolderz中的Authentication设置为null
        SecurityContextHolder.getContext().setAuthentication((Authentication)null);

    // 缓存用户的前一步请求的url
        this.requestCache.saveRequest(request, response);
        this.logger.debug("Calling Authentication entry point.");
    //  调用接口authenticationEntryPoint的commence方法处理异常的后续操作
        this.authenticationEntryPoint.commence(request, response, reason);
    }

2,如果是AccessDeniedException,先判断是否是匿名用户,如果是,也将跳转到登录页面,如果是已认证用户,则交给accessDeniedHandler处理。

if (!this.authenticationTrustResolver.isAnonymous(authentication) && !this.authenticationTrustResolver.isRememberMe(authentication)) {
                this.logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
                this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
            } else {
           // 如果是匿名用户,也将跳转到登录页面
                this.logger.debug("Access is denied (user is " + (this.authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point", exception);
                this.sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
            }

针对上诉总结:

 ExceptionTranslationFilter 是Spring Security的核心filter之一,用来处理AuthenticationException和AccessDeniedException两种异常。

在我们的例子中,AuthenticationException指的是未登录状态下访问受保护资源,AccessDeniedException指的是登陆了但是由于权限不足(比如普通用户访问管理员界面)。

ExceptionTranslationFilter 持有两个处理类,分别是AuthenticationEntryPoint和AccessDeniedHandler。

ExceptionTranslationFilter 对异常的处理是通过这两个处理类实现的,处理规则很简单:

规则1. 如果异常是 AuthenticationException,使用 AuthenticationEntryPoint 处理
规则2. 如果异常是 AccessDeniedException 且用户是匿名用户,使用 AuthenticationEntryPoint 处理
规则3. 如果异常是 AccessDeniedException 且用户不是匿名用户,如果否则交给 AccessDeniedHandler 处理。
 

AccessDeniedHandler 默认实现是 AccessDeniedHandlerImpl。

AuthenticationEntryPoint 默认实现是 LoginUrlAuthenticationEntryPoint, 该类的处理是转发或重定向到登录页面

如果想替换默认的,直接在写这段话

 http.exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint).accessDeniedHandler(myAccessDeniedHandler);

因为exceptionHandling这个类提供了相应的set方法

猜你喜欢

转载自blog.csdn.net/qq_18416057/article/details/88062156