目录

1.1     intercept-methods定义方法权限控制

1.2     使用pointcut定义方法权限控制

1.3     使用注解定义方法权限控制

1.3.1    JSR-250注解

1.3.2    @Secured注解

1.3.3    支持表达式的注解

1.4     方法权限控制的拦截器

1.4.1    MethodSecurityInterceptor

1.4.2    AspectJMethodSecurityInterceptor

之前介绍的都是基于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所写)

Spring Security(17)——基于方法的权限控制的更多相关文章

  1. 后端基于方法的权限控制--Spirng-Security

    后端基于方法的权限控制--Spirng-Security 默认情况下, Spring Security 并不启用方法级的安全管控. 启用方法级的管控后, 可以针对不同的方法通过注解设置不同的访问条件: ...

  2. 在struts2.3.4.1中使用注解、反射、拦截器实现基于方法的权限控制

    权限控制是每一个系统都应该有的一个功能,有些只需要简单控制一下就可以了,然而有些却需要进行更加深入和细致的权限控制,尤其是对于一些MIS类系统,基于方法的权限控制就更加重要了. 用反射和自定义注解来实 ...

  3. Spring Security教程之基于方法的权限控制(十二)

    目录 1.1     intercept-methods定义方法权限控制 1.2     使用pointcut定义方法权限控制 1.3     使用注解定义方法权限控制 1.3.1    JSR-25 ...

  4. Spring Security实现基于RBAC的权限表达式动态访问控制

    昨天有个粉丝加了我,问我如何实现类似shiro的资源权限表达式的访问控制.我以前有一个小框架用的就是shiro,权限控制就用了资源权限表达式,所以这个东西对我不陌生,但是在Spring Securit ...

  5. springboot整合security实现基于url的权限控制

    权限控制基本上是任何一个web项目都要有的,为此spring为我们提供security模块来实现权限控制,网上找了很多资料,但是提供的demo代码都不能完全满足我的需求,因此自己整理了一版. 在上代码 ...

  6. Spring Security实现统一登录与权限控制

    1  项目介绍 最开始是一个单体应用,所有功能模块都写在一个项目里,后来觉得项目越来越大,于是决定把一些功能拆分出去,形成一个一个独立的微服务,于是就有个问题了,登录.退出.权限控制这些东西怎么办呢? ...

  7. Spring Boot+Spring Security+JWT 实现 RESTful Api 权限控制

    摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的.现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能 ...

  8. Spring Security(16)——基于表达式的权限控制

    目录 1.1      通过表达式控制URL权限 1.2      通过表达式控制方法权限 1.2.1     使用@PreAuthorize和@PostAuthorize进行访问控制 1.2.2   ...

  9. Spring Security教程之基于表达式的权限控制(九)

    目录 1.1      通过表达式控制URL权限 1.2      通过表达式控制方法权限 1.2.1     使用@PreAuthorize和@PostAuthorize进行访问控制 1.2.2   ...

随机推荐

  1. #define命令的一些高级用法

    =========================================================== define中的三个特殊符号:#,##,#@ ================= ...

  2. 总结C++中取成员函数地址的几种方法

    这里, 我整理了4种C++中取成员函数地址的方法, 第1,2,4种整理于网上的方法, 第3种cdecl_cast是我自己想到的. 其中, 第4种(汇编)的方法不能在VC6上编译通过. 推荐使用第1,2 ...

  3. JS关闭当前页面的方法

    JS关闭当前页面的方法 一.不带任何提示关闭窗口的js代码 1 <a href="javascript:window.opener=null;window.open('','_self ...

  4. Chrome浏览器网页截全屏算法以及实现

    做个一个简单的批量下载插件叫“挖一下”, 正如插件的名字一样,采集网页里面的所有图片,根据筛选条件过滤不需要的图片,最后下载选中的图片. 索性把网页也一起给截了,截屏分两种: 1.可见内容截屏 2.完 ...

  5. [Android开发常见问题-11] Unable to execute dex: Multiple dex files define 解决方法

    最近在开发一个工程,其中用到了一个开源的库项目Android-ViewPagerIndicator. 这个项目是作为一个库出现的,如下图: 这个项目中包含了android-support-v4.jar ...

  6. jsp传值乱码解决办法

    在jsp中,我们经常从数据库读取数据返回客户端,但我们常常在制作时出现乱码现象,所以我们可以用<%request.setCharacterEncoding("UTF-8"); ...

  7. PHP之语言基础01 By ACReaper

    1.PHP中的变量是不需要声明类型的,由$标识变量,变量的命名规则也是字母或者下划线开头,接着任意字符或者下划线. $PI = 3.14; $radius = 5; $cir = $PI * 2 * ...

  8. Liferay的架构:缓存(第一部分)

    这次,我将要涉及到一个非常重要的概念:缓存.在当今的web应用中,如果没有设计一个比较好的缓存系统,在web中就不可能有一个良好的性能.所以我将要 提到的缓存不仅仅能够更好地理解Liferay架构,而 ...

  9. oc之对象作为类的属性

    对象做为类的属性 1. 一个Target中的类无法直接在另外1个Target中访问. 2. 类的属性代表什么? a. 类的属性代表这类事物具有的共同的特征 b. 类的属性代表这个类所拥有的东西. 灯: ...

  10. PHP常用之封装分页工具类

    分页基本上是每个项目都会使用到的,所以呢,把它封装成一个工具类,以后直接调用就可以了(虽然TP框架的灰常强大,但是自己封一个也未尝不可.),这样既省时又省力还赚'工分'. 我封的这个分页工具类还比较完 ...