25. 安全对象实施

25.1 AOPunion(方法调用)安全拦截器

在 Spring Security 2.0 之前,保护MethodInvocation s 需要大量样板配置。现在,推荐的方法安全性方法是使用namespace configuration。这样,方法安全性基础结构 bean 将自动为您配置,因此您实际上不需要了解实现类。我们将仅简要介绍此处涉及的类。

使用MethodSecurityInterceptor实施方法安全性,可确保MethodInvocation s 的安全。取决于配置方法,拦截器可能特定于单个 bean,也可能在多个 bean 之间共享。拦截器使用MethodSecurityMetadataSource实例获取适用于特定方法调用的配置属性。 MapBasedMethodSecurityMetadataSource用于存储以方法名称作为关键字的配置属性(可以通配),当在属性中使用<intercept-methods><protect-point>元素定义属性时,将在内部使用MapBasedMethodSecurityMetadataSource。其他实现将用于处理基于 Comments 的配置。

25.1.1 显式方法 SecurityInterceptor 配置

当然,您可以直接在应用程序上下文中配置MethodSecurityIterceptor,以与 Spring AOP 的代理机制之一配合使用:

<bean id="bankManagerSecurity" 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">
	<sec:method-security-metadata-source>
	<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
	<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
	</sec:method-security-metadata-source>
</property>
</bean>

25.2 AspectJ(JoinPoint)安全拦截器

AspectJ 安全拦截器与上一节中讨论的 AOP Alliance 安全拦截器非常相似。实际上,我们将仅讨论本节中的区别。

AspectJ 拦截器的名称为AspectJSecurityInterceptor。与 AOP Alliance 安全拦截器不同,后者依赖于 Spring 应用程序上下文通过代理在安全拦截器中进行编织,而AspectJSecurityInterceptor是通过 AspectJ 编译器进行编织的。在同一应用程序中同时使用两种类型的安全拦截器并不少见,其中AspectJSecurityInterceptor用于域对象实例安全,而 AOPunionMethodSecurityInterceptor用于服务层安全。

让我们首先考虑如何在 Spring 应用程序上下文中配置AspectJSecurityInterceptor

<bean id="bankManagerSecurity" class=
	"org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
	<sec:method-security-metadata-source>
	<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
	<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
	</sec:method-security-metadata-source>
</property>
</bean>

如您所见,除了类名之外,AspectJSecurityInterceptor与 AOP Alliance 安全拦截器完全相同。实际上,这两个拦截器可以共享相同的securityMetadataSource,因为SecurityMetadataSourcejava.lang.reflect.Method一起使用,而不是 AOP 库特定的类。当然,您的访问决策可以访问相关的 AOP 库特定的调用(即MethodInvocationJoinPoint),因此在制定访问决策(例如方法参数)时可以考虑一系列附加条件。

接下来,您需要定义 AspectJ aspect。例如:

package org.springframework.security.samples.aspectj;

import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.access.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的每个实例,这是一个未显示的抽象类(您可以使用任意其他类或pointcut表达式)。对于那些好奇的人,需要AspectJCallback,因为proceed();语句仅在around()主体内具有特殊含义。 AspectJSecurityInterceptor在希望目标对象 continue 时调用此匿名AspectJCallback类。

您将需要配置 Spring 以加载方面并将其与AspectJSecurityInterceptor连接。实现此目的的 bean 声明如下所示:

<bean id="domainObjectInstanceSecurityAspect"
	class="security.samples.aspectj.DomainObjectInstanceSecurityAspect"
	factory-method="aspectOf">
<property name="securityInterceptor" ref="bankManagerSecurity"/>
</bean>

而已!现在,您可以使用自己认为合适的任何方式(例如new Person();)从应用程序中的任何位置创建 bean,并且将应用安全拦截器。