在Spring Security 2.0之前,安全MethodInvocation需要进行很多厚重的配置。
现在为方法安全推荐的方式,是使用命名空间配置.
使用这个方式,会自动为你配置好方法安全基础bean,,你不需要了解那些实现类。
我们只需要对这些类提供一个在这里提到的快速概述。
方法安全强制使用 MethodSecurityInterceptor,它会保障 MethodInvocation。
依靠配置方法,一个拦截器可能作用于一个单独的bean或者在多个bean之间共享。
拦截器使用MethodSecurityMetadataSource实例获得配置属性,应用特定的方法调用。
MapBasedMethodSecurityMetadataSource通过方法名(也可以是通配符)保存配置属性关键字,
当使用<intercept-methods> 或<protect-point>元素把属性定义在application context里,将在内部使用。
其他实现会用来处理基于注解的配置。
你可以使用一个Spring AOP代理机制,直接在你的application context里配置一个 MethodSecurityIterceptor :
<bean id="bankManagerSecurity"
class="org.springframework.security.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<value>
com.mycompany.BankManager.delete*=ROLE_SUPERVISOR
com.mycompany.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR
</value>
</property>
</bean>
AspectJ 安全拦截器相对于上面讨论的AOP联盟安全拦截器,就非常简单了。 事实上,我们这节只讨论不同的部分。
AspectJ 拦截器的名字是 AspectJSecurityInterceptor。
与AOP联盟安全拦截器不同,在Spring的application context中的安全拦截器通过代理织入,AspectJSecurityInterceptor是通过AspectJ编译器织入。
在一个系统里使用两种类型的安全拦截器也是常见的,使用AspectJSecurityInterceptor处理领域对象实例的安全,AOP联盟MethodSecurityInterceptor用来处理服务层安全。
让我们首先考虑如何把AspectJSecurityInterceptor配置到spring的application context里:
<bean id="bankManagerSecurity"
class="org.springframework.security.intercept.aspectj.AspectJSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<value>
com.mycompany.BankManager.delete*=ROLE_SUPERVISOR
com.mycompany.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR
</value>
</property>
</bean> 像你看到的,除了类名的部分,AspectJSecurityInterceptor其实与AOP联盟安全拦截器一样。
实际上,两个拦截器共享同样的securityMetadataSource,securityMetadataSource运行的时候使用java.lang.reflect.Method而不是AOP库特定的类。
当然,你的访问表决,已经获得了AOP库具体的引用(比如MethodInvocation 或 JoinPoint),也可以考虑在使用防伪决议时进行更精确的判断(比如利用方法参数)。
下一步,你需要定义一个 AspectJ 切面。比如:
package org.springframework.security.samples.aspectj;
import org.springframework.security.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.intercept.aspectj.AspectJCallback;
import org.springframework.beans.factory.InitializingBean;
public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
private AspectJSecurityInterceptor securityInterceptor;
pointcut domainObjectInstanceExecution(): target(PersistableEntity)
&& execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect);
Object around(): domainObjectInstanceExecution() {
if (this.securityInterceptor == null) {
return proceed();
}
AspectJCallback callback = new AspectJCallback() {
public Object proceedWithObject() {
return proceed();
}
};
return this.securityInterceptor.invoke(thisJoinPoint, callback);
}
public AspectJSecurityInterceptor getSecurityInterceptor() {
return securityInterceptor;
}
public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
public void afterPropertiesSet() throws Exception {
if (this.securityInterceptor == null)
throw new IllegalArgumentException("securityInterceptor required");
}
}在上面例子里,安全拦截器会作用在每一个PersistableEntity实例上,这是没提到过的一个抽象类(你可以使用任何其他的类或你喜欢的切点)。
对于那些情况AspectJCallback是必须的,因为proceed();语句只有在around()内部才有意义。
AspectJSecurityInterceptor在想继续执行目标对象时,调用这个匿名AspectJCallback类。
你会需要配置Spring读取切面,织入到AspectJSecurityInterceptor中。
下面的声明会处理这个:
<bean id="domainObjectInstanceSecurityAspect"
class="org.springframework.security.samples.aspectj.DomainObjectInstanceSecurityAspect"
factory-method="aspectOf">
<property name="securityInterceptor"><ref bean="aspectJSecurityInterceptor"/></property>
</bean>
就是这个了!现在你可以在你的系统里任何地方创建bean了,无论用你想到的什么方式(比如new
Person();),他们都会被安全拦截器处理。