Spring Security(17)——基於方法的權限控制

目錄spring

1.1     intercept-methods定義方法權限控制express

1.2     使用pointcut定義方法權限控制api

1.3     使用註解定義方法權限控制數組

1.3.1    JSR-250註解post

1.3.2    @Secured註解代理

1.3.3    支持表達式的註解對象

1.4     方法權限控制的攔截器繼承

1.4.1    MethodSecurityInterceptor接口

1.4.2    AspectJMethodSecurityInterceptorip

 

       以前介紹的都是基於URL的權限控制,Spring Security一樣支持對於方法的權限控制。能夠經過intercept-methods對某個bean下面的方法進行權限控制,也能夠經過pointcut對整個Service層的方法進行統一的權限控制,還能夠經過註解定義對單獨的某一個方法進行權限控制。

 

1.1     intercept-methods定義方法權限控制

       intercept-methods是須要定義在bean元素下的,經過它能夠定義對當前的bean的某些方法進行權限控制,具體方法是使用其下的子元素protect進行定義的。protect元素須要指定兩個屬性,access和method,method表示須要攔截的方法名稱,能夠使用通配符,access表示執行對應的方法須要擁有的權限,多個權限之間能夠使用逗號分隔。

   <bean id="userService" class="com.xxx.service.impl.UserServiceImpl">

      <security:intercept-methods>

         <security:protect access="ROLE_USER" method="find*"/>

         <security:protect access="ROLE_ADMIN" method="add*"/>

         <security:protect access="ROLE_ADMIN" method="update*"/>

         <security:protect access="ROLE_ADMIN" method="delete*"/>

      </security:intercept-methods>

   </bean>

 

       在上面的配置中表示在執行UserServiceImpl的方法名以find開始的方法時須要當前用戶擁有ROLE_USER的權限,在執行方法名以add、update或delete開始的方法時須要擁有ROLE_ADMIN的權限。當訪問被拒絕時仍是交由ExceptionTranslationFilter處理,這也就意味着若是用戶未登陸則會引導用戶進行登陸,不然默認將返回403錯誤碼到客戶端。

 

1.2     使用pointcut定義方法權限控制

       基於pointcut的方法權限控制是經過global-method-security下的protect-pointcut來定義的。能夠在global-method-security元素下定義多個protect-pointcut以對不一樣的pointcut使用不一樣的權限控制。

   <security:global-method-security>

      <security:protect-pointcut access="ROLE_READ" expression="execution(* com.elim.*..*Service.find*(..))"/>

      <security:protect-pointcut access="ROLE_WRITE" expression="execution(* com.elim.*..*Service.*(..))"/>

   </security:global-method-security>

 

       上面的定義表示咱們在執行com.elim包或其子包下任意以Service結尾的類,其方法名以find開始的全部方法時都須要用戶擁有ROLE_READ的權限,對於com.elim包或其子包下任意以Service結尾的類的其它方法在執行時都須要ROLE_WRITE的權限。須要注意的是對應的類須要是定義在ApplicationContext中的bean才行。此外同對於URL的權限控制同樣,當定義多個protect-pointcut時更具備特性的應當先定義,由於在pointcut匹配的時候是按照聲明順序進行匹配的,一旦匹配上了後續的將再也不進行匹配了。

 

1.3     使用註解定義方法權限控制

       基於註解的方法權限控制也是須要經過global-method-security元素定義來進行啓用的。Spring Security在方法的權限控制上支持三種類型的註解,JSR-250註解、@Secured註解和支持表達式的註解。這三種註解默認都是沒有啓用的,須要單獨經過global-method-security元素的對應屬性進行啓用。

 

1.3.1   JSR-250註解

       要使用JSR-250註解,首先咱們須要經過設置global-method-security元素的jsr250-annotation=」enabled」來啓用基於JSR-250註解的支持,默認爲disabled。

       <security:global-method-security jsr250-annotations="enabled"/>

 

       此外,還須要確保添加了jsr250-api到咱們的類路徑下。以後就能夠在咱們的Service方法上使用JSR-250註解進行權限控制了。

@Service

@RolesAllowed("ROLE_ADMIN")

public class UserServiceImpl implements UserService {

 

   public void addUser(User user) {

      System.out.println("addUser................" + user);

   }

 

   public void updateUser(User user) {

      System.out.println("updateUser.............." + user);

   }

 

   @RolesAllowed({"ROLE_USER", "ROLE_ADMIN"})

   public User find(int id) {

      System.out.println("find user by id............." + id);

      return null;

   }

 

   public void delete(int id) {

      System.out.println("delete user by id................");

   }

 

   @RolesAllowed("ROLE_USER")

   public List<User> findAll() {

      System.out.println("find all user...............");

      return null;

   }

 

}

 

       上面的代碼表示執行UserServiceImpl裏面全部的方法都須要角色ROLE_ADMIN,其中findAll()方法的執行須要ROLE_USER角色,而find()方法的執行對於ROLE_USER或者ROLE_ADMIN角色均可以。

 

       順便介紹一下JSR-250中對權限支持的註解。

       RolesAllowed表示訪問對應方法時所應該具備的角色。其能夠標註在類上,也能夠標註在方法上,當標註在類上時表示其中全部方法的執行都須要對應的角色,當標註在方法上表示執行該方法時所須要的角色,當方法和類上都使用了@RolesAllowed進行標註,則方法上的@RolesAllowed將覆蓋類上的@RolesAllowed,即方法上的@RolesAllowed將對當前方法起做用。@RolesAllowed的值是由角色名稱組成的數組。

       PermitAll表示容許全部的角色進行訪問,也就是說不進行權限控制。@PermitAll能夠標註在方法上也能夠標註在類上,當標註在方法上時則只對對應方法不進行權限控制,而標註在類上時表示對類裏面全部的方法都不進行權限控制。(1)當@PermitAll標註在類上,而@RolesAllowed標註在方法上時則按照@RolesAllowed將覆蓋@PermitAll,即須要@RolesAllowed對應的角色才能訪問。(2)當@RolesAllowed標註在類上,而@PermitAll標註在方法上時則對應的方法也是不進行權限控制的。(3)當在方法上同時使用了@PermitAll和@RolesAllowed時先定義的將發生做用,而都定義在類上時則是反過來的,即後定義的將發生做用(這個沒多大的實際意義,實際應用中不會有這樣的定義)。

       DenyAll是和PermitAll相反的,表示不管什麼角色都不能訪問。@DenyAll只能定義在方法上。你可能會有疑問使用@DenyAll標註的方法不管擁有什麼權限都不能訪問,那還定義它幹啥呢?使用@DenyAll定義的方法只是在咱們的權限控制中不能訪問,脫離了權限控制仍是能夠訪問的。

 

1.3.2  @Secured註解

       @Secured是由Spring Security定義的用來支持方法權限控制的註解。它的使用也是須要啓用對應的支持纔會生效的。經過設置global-method-security元素的secured-annotations=」enabled」能夠啓用Spring Security對使用@Secured註解標註的方法進行權限控制的支持,其值默認爲disabled。

   <security:global-method-security secured-annotations="enabled"/>

 

@Service

public class UserServiceImpl implements UserService {

 

   @Secured("ROLE_ADMIN")

   public void addUser(User user) {

      System.out.println("addUser................" + user);

   }

 

   @Secured("ROLE_USER")

   public List<User> findAll() {

      System.out.println("find all user...............");

      return null;

   }

 

}

 

       在上面的代碼中咱們使用@Secured定義了只有擁有ROLE_ADMIN角色的用戶才能調用方法addUser(),只有擁有ROLE_USER角色的用戶才能調用方法findAll()。

 

1.3.3   支持表達式的註解

       Spring Security中定義了四個支持使用表達式的註解,分別是@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter。其中前二者能夠用來在方法調用前或者調用後進行權限檢查,後二者能夠用來對集合類型的參數或者返回值進行過濾。要使它們的定義可以對咱們的方法的調用產生影響咱們須要設置global-method-security元素的pre-post-annotations=」enabled」,默認爲disabled。

   <security:global-method-security pre-post-annotations="disabled"/>

 

使用@PreAuthorize@PostAuthorize進行訪問控制

       @PreAuthorize能夠用來控制一個方法是否可以被調用。

@Service

public class UserServiceImpl implements UserService {

 

   @PreAuthorize("hasRole('ROLE_ADMIN')")

   public void addUser(User user) {

      System.out.println("addUser................" + user);

   }

 

   @PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")

   public User find(int id) {

      System.out.println("find user by id............." + id);

      return null;

   }

 

}

       在上面的代碼中咱們定義了只有擁有角色ROLE_ADMIN的用戶才能訪問adduser()方法,而訪問find()方法須要有ROLE_USER角色或ROLE_ADMIN角色。使用表達式時咱們還能夠在表達式中使用方法參數。

public class UserServiceImpl implements UserService {

 

   /**

    * 限制只能查詢Id小於10的用戶

    */

   @PreAuthorize("#id<10")

   public User find(int id) {

      System.out.println("find user by id........." + id);

      return null;

   }

  

   /**

    * 限制只能查詢本身的信息

    */

   @PreAuthorize("principal.username.equals(#username)")

   public User find(String username) {

      System.out.println("find user by username......" + username);

      return null;

   }

 

   /**

    * 限制只能新增用戶名稱爲abc的用戶

    */

   @PreAuthorize("#user.name.equals('abc')")

   public void add(User user) {

      System.out.println("addUser............" + user);

   }

 

}

       在上面代碼中咱們定義了調用find(int id)方法時,只容許參數id小於10的調用;調用find(String username)時只容許username爲當前用戶的用戶名;定義了調用add()方法時只有當參數user的name爲abc時才能夠調用。

 

       有時候可能你會想在方法調用完以後進行權限檢查,這種狀況比較少,可是若是你有的話,Spring Security也爲咱們提供了支持,經過@PostAuthorize能夠達到這一效果。使用@PostAuthorize時咱們能夠使用內置的表達式returnObject表示方法的返回值。咱們來看下面這一段示例代碼。

   @PostAuthorize("returnObject.id%2==0")

   public User find(int id) {

      User user = new User();

      user.setId(id);

      return user;

   }

       上面這一段代碼表示將在方法find()調用完成後進行權限檢查,若是返回值的id是偶數則表示校驗經過,不然表示校驗失敗,將拋出AccessDeniedException。       須要注意的是@PostAuthorize是在方法調用完成後進行權限檢查,它不能控制方法是否能被調用,只能在方法調用完成後檢查權限決定是否要拋出AccessDeniedException。

 

使用@PreFilter@PostFilter進行過濾

       使用@PreFilter和@PostFilter能夠對集合類型的參數或返回值進行過濾。使用@PreFilter和@PostFilter時,Spring Security將移除使對應表達式的結果爲false的元素。

   @PostFilter("filterObject.id%2==0")

   public List<User> findAll() {

      List<User> userList = new ArrayList<User>();

      User user;

      for (int i=0; i<10; i++) {

         user = new User();

         user.setId(i);

         userList.add(user);

      }

      return userList;

   }

 

       上述代碼表示將對返回結果中id不爲偶數的user進行移除。filterObject是使用@PreFilter和@PostFilter時的一個內置表達式,表示集合中的當前對象。當@PreFilter標註的方法擁有多個集合類型的參數時,須要經過@PreFilter的filterTarget屬性指定當前@PreFilter是針對哪一個參數進行過濾的。以下面代碼就經過filterTarget指定了當前@PreFilter是用來過濾參數ids的。

   @PreFilter(filterTarget="ids", value="filterObject%2==0")

   public void delete(List<Integer> ids, List<String> usernames) {

      ...

   }

 

1.4     方法權限控制的攔截器

       關於方法權限控制,Spring Security提供了兩類AbstractSecurityInterceptor,基於AOP Alliance的MethodSecurityInterceptor,和基於Aspectj繼承自MethodSecurityInterceptor的AspectJMethodSecurityInterceptor。

 

1.4.1   MethodSecurityInterceptor

       當咱們在使用基於NameSpace進行方法保護的配置時,Spring Security默認配置的就是MethodSecurityInterceptor。根據配置的不一樣,一個攔截器可能只是針對於一個bean,也多是針對於多個bean的。MethodSecurityInterceptor使用一個MethodSecurityMetadataSource的實例來獲取特定方法調用配置的ConfigAttribute。當咱們在ApplicationContext配置文件中使用intercept-methods元素或protect-point元素定義須要保護的方法調用時,Spring Security內部默認會使用一個MapBasedMethodSecurityMetadataSource來保存在這些元素上定義的配置信息,保存的key是對應的方法名(能夠是含有通配符的)。相似的使用JSR-250註解時將使用Jsr250MethodSecurityMetadataSource解析配置屬性;使用@Secured註解時將使用SecuredAnnotationSecurityMetadataSource解析配置屬性;使用pre-post-annotations時將使用PrePostAnnotationSecurityMetadataSource解析配置屬性。

       MethodSecurityInterceptor是實現了MethodInterceptor接口的,因此咱們在使用Spring Aop時,能夠本身配置一個MethodSecurityInterceptor的bean。

 

   <!-- 自定義MethodSecurityInterceptor -->

   <bean id="methodSecurityInterceptor"

  class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">

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

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

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

      <property name="securityMetadataSource">

         <security:method-security-metadata-source>

            <!-- 指定須要受保護的方法和須要的權限 -->

            <security:protect method="com.xxx.service.UserService.find*"

                access="ROLE_USER" />

            <security:protect method="com.xxx.service.UserService.delete*"

                access="ROLE_ADMIN" />

         </security:method-security-metadata-source>

      </property>

   </bean>

 

       定義了MethodSecurityInterceptor之後,咱們須要相似AOP配置那樣,配置哪些該MethodInterceptor須要攔截哪些方法的執行。這種可選配置是不少種的,由於咱們這裏只是攔截UserService中的具體方法,因此就採用基於bean name的自動代理。

   <!-- 基於bean的攔截 -->

   <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

      <property name="interceptorNames">

         <list>

            <value>methodSecurityInterceptor</value>

         </list>

      </property>

      <property name="beanNames">

         <list>

            <value>userService</value>

         </list>

      </property>

   </bean>

 

       按照上面的配置,咱們在訪問UserService的find方法時就須要ROLE_USER的權限,而訪問delete方法時則須要ROLE_ADMIN權限。

 

1.4.2   AspectJMethodSecurityInterceptor

       AspectJMethodSecurityInterceptor是繼承自MethodSecurityInterceptor的,不一樣的是AspectJMethodSecurityInterceptor是用來支持AspectJ的JointPoint的,但在底層仍是會把它封裝成一個MethodInvocation進行調用。

 

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

相關文章
相關標籤/搜索