Spring Security(15)——權限鑑定結構

目錄spring

1.1      權限jsp

1.2      調用前的處理ide

1.2.1     AccessDecisionManagerurl

1.2.2     基於投票的AccessDecisionManager實現orm

1.3      調用後的處理xml

1.4      角色的繼承對象

 

1.1    權限

       全部的Authentication實現類都保存了一個GrantedAuthority列表,其表示用戶所具備的權限。GrantedAuthority是經過AuthenticationManager設置到Authentication對象中的,而後AccessDecisionManager將從Authentication中獲取用戶所具備的GrantedAuthority來鑑定用戶是否具備訪問對應資源的權限。blog

       GrantedAuthority是一個接口,其中只定義了一個getAuthority()方法,其返回值爲String類型。該方法容許AccessDecisionManager獲取一個可以精確表明該權限的字符串。經過返回一個字符串,一個GrantedAuthority可以很輕易的被大部分AccessDecisionManager讀取。若是一個GrantedAuthority不可以精確的使用一個String來表示,那麼其對應的getAuthority()方法調用應當返回一個null,這表示AccessDecisionManager必須對該GrantedAuthority的實現有特定的支持,從而能夠獲取該GrantedAuthority所表明的權限信息。繼承

       Spring Security內置了一個GrantedAuthority的實現,SimpleGrantedAuthority。它直接接收一個表示權限信息的字符串,而後getAuthority()方法直接返回該字符串。Spring Security內置的全部AuthenticationProvider都是使用它來封裝Authentication對象的。接口

 

1.2     調用前的處理

       Spring Security是經過攔截器來控制受保護對象的訪問的,如方法調用和Web請求。在正式訪問受保護對象以前,Spring Security將使用AccessDecisionManager來鑑定當前用戶是否有訪問對應受保護對象的權限。

 

1.2.1AccessDecisionManager

       AccessDecisionManager是由AbstractSecurityInterceptor調用的,它負責鑑定用戶是否有訪問對應資源(方法或URL)的權限。AccessDecisionManager是一個接口,其中只定義了三個方法,其定義以下。

public interface AccessDecisionManager {

 

    /**

     * 經過傳遞的參數來決定用戶是否有訪問對應受保護對象的權限

     *

     * @param authentication 當前正在請求受包含對象的Authentication

     * @param object 受保護對象,其能夠是一個MethodInvocation、JoinPoint或FilterInvocation。

     * @param configAttributes 與正在請求的受保護對象相關聯的配置屬性

     *

     */

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

        throws AccessDeniedException, InsufficientAuthenticationException;

 

    /**

     * 表示當前AccessDecisionManager是否支持對應的ConfigAttribute

     */

    boolean supports(ConfigAttribute attribute);

 

    /**

     * 表示當前AccessDecisionManager是否支持對應的受保護對象類型

     */

    boolean supports(Class<?> clazz);

}

 

       decide()方法用於決定authentication是否符合受保護對象要求的configAttributes。supports(ConfigAttribute attribute)方法是用來判斷AccessDecisionManager是否可以處理對應的ConfigAttribute的。supports(Class<?> clazz)方法用於判斷配置的AccessDecisionManager是否支持對應的受保護對象類型。

 

1.2.2基於投票的AccessDecisionManager實現

       Spring Security已經內置了幾個基於投票的AccessDecisionManager,固然若是須要你也能夠實現本身的AccessDecisionManager。如下是Spring Security官方文檔提供的一個圖,其展現了與基於投票的AccessDecisionManager實現相關的類。


 


 

       使用這種方式,一系列的AccessDecisionVoter將會被AccessDecisionManager用來對Authentication是否有權訪問受保護對象進行投票,而後再根據投票結果來決定是否要拋出AccessDeniedException。AccessDecisionVoter是一個接口,其中定義有三個方法,具體結構以下所示。

public interface AccessDecisionVoter<S> {

 

    intACCESS_GRANTED = 1;

    intACCESS_ABSTAIN = 0;

    intACCESS_DENIED = -1;

 

    boolean supports(ConfigAttribute attribute);

 

    boolean supports(Class<?> clazz);

 

    int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);

}

 

       vote()方法的返回結果會是AccessDecisionVoter中定義的三個常量之一。ACCESS_GRANTED表示贊成,ACCESS_DENIED表示返回,ACCESS_ABSTAIN表示棄權。若是一個AccessDecisionVoter不能斷定當前Authentication是否擁有訪問對應受保護對象的權限,則其vote()方法的返回值應當爲棄權ACCESS_ABSTAIN。

       Spring Security內置了三個基於投票的AccessDecisionManager實現類,它們分別是AffirmativeBased、ConsensusBased和UnanimousBased。

       AffirmativeBased的邏輯是這樣的:

       (1)只要有AccessDecisionVoter的投票爲ACCESS_GRANTED則贊成用戶進行訪問;

       (2)若是所有棄權也表示經過;

       (3)若是沒有一我的投同意票,可是有人投反對票,則將拋出AccessDeniedException。

       ConsensusBased的邏輯是這樣的:

       (1)若是同意票多於反對票則表示經過。

       (2)反過來,若是反對票多於同意票則將拋出AccessDeniedException。

       (3)若是同意票與反對票相同且不等於0,而且屬性allowIfEqualGrantedDeniedDecisions的值爲true,則表示經過,不然將拋出異常AccessDeniedException。參數allowIfEqualGrantedDeniedDecisions的值默認爲true。

       (4)若是全部的AccessDecisionVoter都棄權了,則將視參數allowIfAllAbstainDecisions的值而定,若是該值爲true則表示經過,不然將拋出異常AccessDeniedException。參數allowIfAllAbstainDecisions的值默認爲false。

       UnanimousBased的邏輯與另外兩種實現有點不同,另外兩種會一次性把受保護對象的配置屬性所有傳遞給AccessDecisionVoter進行投票,而UnanimousBased會一次只傳遞一個ConfigAttribute給AccessDecisionVoter進行投票。這也就意味着若是咱們的AccessDecisionVoter的邏輯是隻要傳遞進來的ConfigAttribute中有一個可以匹配則投同意票,可是放到UnanimousBased中其投票結果就不必定是同意了。UnanimousBased的邏輯具體來講是這樣的:

       (1)若是受保護對象配置的某一個ConfigAttribute被任意的AccessDecisionVoter反對了,則將拋出AccessDeniedException。

       (2)若是沒有反對票,可是有同意票,則表示經過。

       (3)若是所有棄權了,則將視參數allowIfAllAbstainDecisions的值而定,true則經過,false則拋出AccessDeniedException。

 

1.2.2.1RoleVoter

       RoleVoter是Spring Security內置的一個AccessDecisionVoter,其會將ConfigAttribute簡單的看做是一個角色名稱,在投票的時若是擁有該角色即投同意票。若是ConfigAttribute是以「ROLE_」開頭的,則將使用RoleVoter進行投票。當用戶擁有的權限中有一個或多個能匹配受保護對象配置的以「ROLE_」開頭的ConfigAttribute時其將投同意票;若是用戶擁有的權限中沒有一個能匹配受保護對象配置的以「ROLE_」開頭的ConfigAttribute,則RoleVoter將投反對票;若是受保護對象配置的ConfigAttribute中沒有以「ROLE_」開頭的,則RoleVoter將棄權。

 

1.2.2.2AuthenticatedVoter

       AuthenticatedVoter也是Spring Security內置的一個AccessDecisionVoter實現。其主要用來區分匿名用戶、經過Remember-Me認證的用戶和徹底認證的用戶。徹底認證的用戶是指由系統提供的登陸入口進行成功登陸認證的用戶。

       AuthenticatedVoter能夠處理的ConfigAttribute有IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED和IS_AUTHENTICATED_ANONYMOUSLY。若是ConfigAttribute不在這三者範圍以內,則AuthenticatedVoter將棄權。不然將視ConfigAttribute而定,若是ConfigAttribute爲IS_AUTHENTICATED_ANONYMOUSLY,則無論用戶是匿名的仍是已經認證的都將投同意票;若是是IS_AUTHENTICATED_REMEMBERED則僅當用戶是由Remember-Me自動登陸,或者是經過登陸入口進行登陸認證時纔會投同意票,不然將投反對票;而當ConfigAttribute爲IS_AUTHENTICATED_FULLY時僅當用戶是經過登陸入口進行登陸的纔會投同意票,不然將投反對票。

       AuthenticatedVoter是經過AuthenticationTrustResolver的isAnonymous()方法和isRememberMe()方法來判斷SecurityContextHolder持有的Authentication是否爲AnonymousAuthenticationToken或RememberMeAuthenticationToken的,便是否爲IS_AUTHENTICATED_ANONYMOUSLY和IS_AUTHENTICATED_REMEMBERED。

 

1.2.2.3自定義Voter

       固然,用戶也能夠經過實現AccessDecisionVoter來實現本身的投票邏輯。

 

1.3     調用後的處理

       AccessDecisionManager是用來在訪問受保護的對象以前判斷用戶是否擁有訪問該對象的權限。有的時候咱們可能會但願在請求執行完成後對返回值作一些修改,固然,你能夠簡單的經過AOP來實現這一功能。Spring Security爲咱們提供了一個AfterInvocationManager接口,它容許咱們在受保護對象訪問完成後對返回值進行修改或者進行權限鑑定,看是否須要拋出AccessDeniedException,其將由AbstractSecurityInterceptor的子類進行調用。AfterInvocationManager接口的定義以下。

public interface AfterInvocationManager {

  

    Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,

        Object returnedObject) throws AccessDeniedException;

 

    boolean supports(ConfigAttribute attribute);

 

    boolean supports(Class<?> clazz);

}

 

       如下是Spring Security官方文檔提供的一個AfterInvocationManager構造實現的圖。



 

 

       相似於AuthenticationManager,AfterInvocationManager擁有一個默認的實現類AfterInvocationProviderManager,其中擁有一個由AfterInvocationProvider組成的集合,AfterInvocationProvider與AfterInvocationManager具備相同的方法定義,在調用AfterInvocationProviderManager中的方法時實際上就是依次調用其中包含的AfterInvocationProvider對應的方法。

       須要注意的是AfterInvocationManager須要在受保護對象成功被訪問後才能執行。

 

1.4     角色的繼承

       對於角色繼承這種需求也是常常有的,好比要求ROLE_ADMIN將擁有全部的ROLE_USER所具備的權限。固然咱們能夠給擁有ROLE_ADMIN角色的用戶同時授予ROLE_USER角色來達到這一效果或者修改須要ROLE_USER進行訪問的資源使用ROLE_ADMIN也能夠訪問。Spring Security爲咱們提供了一種更爲簡便的辦法,那就是角色的繼承,它容許咱們的ROLE_ADMIN直接繼承ROLE_USER,這樣全部ROLE_USER能夠訪問的資源ROLE_ADMIN也能夠訪問。定義角色的繼承咱們須要在ApplicationContext中定義一個RoleHierarchy,而後再把它賦予給一個RoleHierarchyVoter,以後再把該RoleHierarchyVoter加入到咱們基於Voter的AccessDecisionManager中,並指定當前使用的AccessDecisionManager爲咱們本身定義的那個。如下是一個定義角色繼承的完整示例。

<beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:security="http://www.springframework.org/schema/security"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

          http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

          http://www.springframework.org/schema/security

          http://www.springframework.org/schema/security/spring-security-3.1.xsd">

 

   <!-- 經過access-decision-manager-ref指定將要使用的AccessDecisionManager -->

   <security:http access-decision-manager-ref="accessDecisionManager">

      <security:form-login/>

      <security:intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />

      <security:intercept-url pattern="/**" access="ROLE_USER" />

   </security:http>

 

   <security:authentication-manager alias="authenticationManager">

      <security:authentication-provider

         user-service-ref="userDetailsService"/>

   </security:authentication-manager>

 

   <bean id="userDetailsService"

      class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">

      <property name="dataSource" ref="dataSource" />

   </bean>

   <!-- 本身定義AccessDecisionManager對應的bean -->

   <bean id="accessDecisionManager"class="org.springframework.security.access.vote.AffirmativeBased">

      <property name="decisionVoters">

         <list>

            <ref local="roleVoter"/>

         </list>

      </property>

   </bean>

 

   <bean id="roleVoter"

      class="org.springframework.security.access.vote.RoleHierarchyVoter">

      <constructor-arg ref="roleHierarchy" />

   </bean>

   <bean id="roleHierarchy"

      class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">

      <property name="hierarchy"><!-- 角色繼承關係 -->

         <value>

            ROLE_ADMIN > ROLE_USER

         </value>

      </property>

   </bean>

 

</beans>

 

       在上述配置中咱們就定義好了ROLE_ADMIN是繼承自ROLE_USER的,這樣ROLE_ADMIN將可以訪問全部ROLE_USER能夠訪問的資源。經過RoleHierarchyImpl的hierarchy屬性咱們能夠定義多個角色之間的繼承關係,如:

   <bean id="roleHierarchy"

      class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">

      <property name="hierarchy"><!-- 角色繼承關係 -->

         <value>

            ROLE_ADMIN > ROLE_USER

            ROLE_A > ROLE_B

            ROLE_B > ROLE_C

            ROLE_C > ROLE_D

         </value>

      </property>

   </bean>

 

       在上述配置咱們同時定義了ROLE_ADMIN繼承了ROLE_USER,ROLE_A繼承了ROLE_B,ROLE_B又繼承了ROLE_C,ROLE_C又繼承了ROLE_D,這樣ROLE_A將能訪問ROLE_B、ROLE_C和ROLE_D所能訪問的全部資源。

 

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

相關文章
相關標籤/搜索