Spring Security(14)——權限鑒定基礎


目錄

1.1     Spring Security的AOP Advice思想

1.2     AbstractSecurityInterceptor

1.2.1    ConfigAttribute

1.2.2    RunAsManager

1.2.3    AfterInvocationManager

 

       Spring Security的權限鑒定是由AccessDecisionManager接口負責的。具體來說是由其中的decide()方法負責,其定義如下。

    void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)

        throws AccessDeniedException, InsufficientAuthenticationException;

 

       如你所見,該方法接收三個參數,第一個參數是包含當前用戶信息的Authentication對象;第二個參數表示當前正在請求的受保護的對象,基本上來說是MethodInvocation(使用AOP)、JoinPoint(使用Aspectj)和FilterInvocation(Web請求)三種類型;第三個參數表示與當前正在訪問的受保護對象的配置屬性,如一個角色列表。

 

1.1     Spring Security的AOP Advice思想

       對於使用AOP而言,我們可以使用幾種不同類型的advice:before、after、throws和around。其中around advice是非常實用的,通過它我們可以控制是否要執行方法、是否要修改方法的返回值,以及是否要拋出異常。Spring Security在對方法調用和Web請求時也是使用的around advice的思想。在方法調用時,可以使用標准的Spring AOP來達到around advice的效果,而在進行Web請求時是通過標准的Filter來達到around advice的效果。

       對於大部分人而言都比較喜歡對Service層的方法調用進行權限控制,因為我們的主要業務邏輯都是在Service層進行實現的。如果你只是想保護Service層的方法,那么使用Spring AOP就可以了。如果你需要直接保護領域對象,那么你可以考慮使用Aspectj。

       你可以選擇使用Aspectj或Spring AOP對方法調用進行鑒權,或者選擇使用Filter對Web請求進行鑒權。當然,你也可以選擇使用這三種方式的任意組合進行鑒權。通常的做法是使用Filter對Web請求進行一個比較粗略的鑒權,輔以使用Spring AOP對Service層的方法進行較細粒度的鑒權。

 

1.2     AbstractSecurityInterceptor

       AbstractSecurityInterceptor是一個實現了對受保護對象的訪問進行攔截的抽象類,其中有幾個比較重要的方法。beforeInvocation()方法實現了對訪問受保護對象的權限校驗,內部用到了AccessDecisionManager和AuthenticationManager;finallyInvocation()方法用於實現受保護對象請求完畢后的一些清理工作,主要是如果在beforeInvocation()中改變了SecurityContext,則在finallyInvocation()中需要將其恢復為原來的SecurityContext,該方法的調用應當包含在子類請求受保護資源時的finally語句塊中;afterInvocation()方法實現了對返回結果的處理,在注入了AfterInvocationManager的情況下默認會調用其decide()方法。AbstractSecurityInterceptor只是提供了這幾種方法,並且包含了默認實現,具體怎么調用將由子類負責。每一種受保護對象都擁有繼承自AbstractSecurityInterceptor的攔截器類, MethodSecurityInterceptor將用於調用受保護的方法,而FilterSecurityInterceptor將用於受保護的Web請求。它們在處理受保護對象的請求時都具有一致的邏輯,具體的邏輯如下。

       1、先將正在請求調用的受保護對象傳遞給beforeInvocation()方法進行權限鑒定。

       2、權限鑒定失敗就直接拋出異常了。

       3、鑒定成功將嘗試調用受保護對象,調用完成后,不管是成功調用,還是拋出異常,都將執行finallyInvocation()。

       4、如果在調用受保護對象后沒有拋出異常,則調用afterInvocation()。

 

       以下是MethodSecurityInterceptor在進行方法調用的一段核心代碼。

    public Object invoke(MethodInvocation mi) throws Throwable {

        InterceptorStatusToken token = super.beforeInvocation(mi);

 

        Object result;

        try {

            result = mi.proceed();

        } finally {

            super.finallyInvocation(token);

        }

        returnsuper.afterInvocation(token, result);

    }

 

1.2.1   ConfigAttribute

       AbstractSecurityInterceptor的beforeInvocation()方法內部在進行鑒權的時候使用的是注入的AccessDecisionManager的decide()方法進行的。如前所述,decide()方法是需要接收一個受保護對象對應的ConfigAttribute集合的。一個ConfigAttribute可能只是一個簡單的角色名稱,具體將視AccessDecisionManager的實現者而定。AbstractSecurityInterceptor將使用一個SecurityMetadataSource對象來獲取與受保護對象關聯的ConfigAttribute集合,具體SecurityMetadataSource將由子類實現提供。ConfigAttribute將通過注解的形式定義在受保護的方法上,或者通過access屬性定義在受保護的URL上。例如我們常見的<intercept-url pattern=”/**” access=”ROLE_USER,ROLE_ADMIN”/>就表示將ConfigAttribute ROLE_USER和ROLE_ADMIN應用在所有的URL請求上。對於默認的AccessDecisionManager的實現,上述配置意味着用戶所擁有的權限中只要擁有一個GrantedAuthority與這兩個ConfigAttribute中的一個進行匹配則允許進行訪問。當然,嚴格的來說ConfigAttribute只是一個簡單的配置屬性而已,具體的解釋將由AccessDecisionManager來決定。

 

1.2.2  RunAsManager

       在某些情況下你可能會想替換保存在SecurityContext中的Authentication。這可以通過RunAsManager來實現的。在AbstractSecurityInterceptor的beforeInvocation()方法體中,在AccessDecisionManager鑒權成功后,將通過RunAsManager在現有Authentication基礎上構建一個新的Authentication,如果新的Authentication不為空則將產生一個新的SecurityContext,並把新產生的Authentication存放在其中。這樣在請求受保護資源時從SecurityContext中獲取到的Authentication就是新產生的Authentication。待請求完成后會在finallyInvocation()中將原來的SecurityContext重新設置給SecurityContextHolder。AbstractSecurityInterceptor默認持有的是一個對RunAsManager進行空實現的NullRunAsManager。此外,Spring Security對RunAsManager有一個還有一個非空實現類RunAsManagerImpl,其在構造新的Authentication時是這樣的邏輯:如果受保護對象對應的ConfigAttribute中擁有以“RUN_AS_”開頭的配置屬性,則在該屬性前加上“ROLE_”,然后再把它作為一個GrantedAuthority賦給將要創建的Authentication(如ConfigAttribute中擁有一個“RUN_AS_ADMIN”的屬性,則將構建一個“ROLE_RUN_AS_ADMIN”的GrantedAuthority),最后再利用原Authentication的principal、權限等信息構建一個新的Authentication進行返回;如果不存在任何以“RUN_AS_”開頭的ConfigAttribute,則直接返回null。RunAsManagerImpl構建新的Authentication的核心代碼如下所示。

    public Authentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {

        List<GrantedAuthority> newAuthorities = new ArrayList<GrantedAuthority>();

        for (ConfigAttribute attribute : attributes) {

            if (this.supports(attribute)) {

                GrantedAuthority extraAuthority = newSimpleGrantedAuthority(getRolePrefix() + attribute.getAttribute());

                newAuthorities.add(extraAuthority);

            }

        }

        if (newAuthorities.size() == 0) {

            returnnull;

        }

        // Add existing authorities

        newAuthorities.addAll(authentication.getAuthorities());

        returnnew RunAsUserToken(this.key, authentication.getPrincipal(), authentication.getCredentials(),

                newAuthorities, authentication.getClass());

    }

 

1.2.3  AfterInvocationManager

       在請求受保護的對象完成以后,可以通過afterInvocation()方法對返回值進行修改。AbstractSecurityInterceptor把對返回值進行修改的控制權交給其所持有的AfterInvocationManager了。AfterInvocationManager可以選擇對返回值進行修改、不修改或拋出異常(如:后置權限鑒定不通過)。

 

       以下是Spring Security官方文檔提供的一張關於AbstractSecurityInterceptor相關關系的圖。



 

 

 

(注:本文是基於Spring Security3.1.6所寫)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM