第 33 章 保护方法调用

提示

因为Spring Security对方法调用和ACL权限控制的实现都是基于Spring的AOP,所以只能保护定义在applicationContext.xml中的Java类,对直接new出来的对象是无法保护的。

这里有三种方式可以选择:

33.1. 控制全局范围的方法权限

使用global-method-security和protect-point标签来管理全局范围的方法权限。

为了在spring中使用AOP,我们要为项目添加几个依赖库。

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib-nodep</artifactId>
  <version>2.2.2</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.7.1</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.7.1</version>
</dependency>
        

首先来看看我们将要保护的java类。


package com.family168.springsecuritybook.ch201;

public class MessageServiceImpl implements MessageService {
    public String adminMessage() {
        return "admin message";
    }

    public String adminDate() {
        return "admin " + System.currentTimeMillis();
    }
    public String userMessage() {
        return "user message";
    }

    public String userDate() {
        return "user " + System.currentTimeMillis();
    }
}
        

这里使用的是spring-2.0中的aop语法,对MessageService中所有以admin开头的方法进行权限控制,限制这些方法只能由ROLE_ADMIN调用。

<global-method-security>
    <protect-pointcut
        expression="execution(* com.family168.springsecuritybook.ch201.MesageServiceImpl.admin*(..))"
        access="ROLE_ADMIN"/>
</global-method-security>
        

现在只有拥有ROLE_ADMIN权限的用户才能调用MessageService中以admin开头的方法了,当我们以user/user登陆系统时,尝试调用MessageService类的adminMessage()会跑出一个“访问被拒绝”的异常。

33.2. 控制某个bean内的方法权限

在bean中嵌入intercept-methods和protect标签。

这需要改造配置文件。

<beans:bean id="messageService" class="com.family168.springsecuritybook.ch201.MessageServiceImpl">
    <intercept-methods>
        <protect access="ROLE_ADMIN" method="userMessage"/>
    </intercept-methods>
</beans:bean>
        

现在messageService中的userMessage()方法只允许拥有ROLE_ADMIN权限的用户才能调用了。

使用intercept-methods面临着几个问题

首先,intercept-methods只能使用jdk14的方式拦截实现了接口的类,而不能用cglib直接拦截无接口的类。

其次,intercept-methods和global-method-security一起使用,同时使用时,global-method-security一切正常,intercept-methods则会完全不起作用。

33.3. 使用annotation控制方法权限

借助jdk5以后支持的annotation,我们直接在代码中设置某一方法的调用权限。

现在有两种选择,使用Spring Security提供的Secured注解,或者使用jsr250规范中定义的注解。

33.3.1. 使用Secured

首先修改global-method-security中的配置,添加支持annotation的参数。

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

spring security 3默认支持jdk5风格的注解,下面我们可以随便在java代码中添加注解了。

package com.family168.springsecuritybook.ch201;

import org.springframework.security.annotation.Secured;

public class MessageServiceImpl implements MessageService {
    @Secured({"ROLE_ADMIN", "ROLE_USER"})
    public String userMessage() {
        return "user message";
    }
}
            

在Secured中设置了ROLE_ADMIN和ROLE_USER两个权限,只要当前用户拥有其中任意一个权限都可以调用这个方法。

33.3.2. 使用jsr250

首先还是要修改配置文件。

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

然后添加依赖包。

<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>jsr250-api</artifactId>
  <version>1.0</version>
</dependency>
            

现在可以在代码中使用jsr250中的注解了。

package com.family168.springsecuritybook.ch201;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;

public class MessageServiceImpl implements MessageService {
    @RolesAllowed({"ROLE_ADMIN", "ROLE_USER"})1
    public String userMessage() {
        return "user message";
    }

    @DenyAll2
    public String userMessage2() {
        return "user message";
    }

    @PermitAll3
    public String userMessage2() {
        return "user message";
    }
}
            

1

RolesAllowed与前面的Secured功能相同,用户只要满足其中定义的权限之一就可以调用方法。

2

DenyAll拒绝所有的用户调用方法。

3

PermitAll允许所有的用户调用方法。

从实际使用上来讲,jsr250里多出来的DenyAll和PermitAll纯属浪费,谁会定义谁也不能调用的方法呢?实际上,要是annotation支持布尔操作就好了,比如逻辑并,逻辑或,逻辑否之类的。

还有jsr250中未被支持的RunAs注解,如果能利用起来估计更有趣。

实例在ch201。