spring security 4 filter ExceptionTranslationFilter(二)

今天學習的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方法

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章