# 安全对象实现

##  安全拦截器

在 Spring Security2.0 之前，保护`MethodInvocation`s 需要相当多的锅炉板配置。现在推荐的方法安全性是使用[名称空间配置](../configuration/xml-namespace.html#ns-method-security)。通过这种方式，方法安全基础设施 bean 将自动为你配置，因此你实际上不需要了解实现类。我们将提供这里涉及的类的简要概述。

方法安全性是使用`MethodSecurityInterceptor`来实现的，它保护`MethodInvocation`s。根据配置方法，拦截器可以特定于单个 Bean 或在多个 bean 之间共享。拦截器使用`MethodSecurityMetadataSource`实例来获得配置属性应用于特定方法调用。`MapBasedMethodSecurityMetadataSource`用于存储由方法名（可以使用通配符）键控的配置属性，并且当在应用程序上下文中使用`<intercept-methods>`或`<protect-point>`元素定义属性时，将在内部使用。其他实现将用于处理基于注释的配置。

### 显式方法 SecurityInterceptor 配置

当然，你可以在应用程序上下文中直接配置`MethodSecurityInterceptor`，以便与 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>
```

##  安全拦截器

AspectJ 安全拦截器与上一节讨论的 AOP Alliance 安全拦截器非常相似。事实上，我们只在这一节讨论分歧。

AspectJ 拦截器被命名为`AspectJSecurityInterceptor`。 AOP Alliance Security Interceptor 依赖 Spring 应用程序上下文通过代理在安全拦截器中进行编织，而`AspectJSecurityInterceptor`则通过 AspectJ 编译器进行编织。在同一个应用程序中使用两种类型的安全拦截器并不少见，`AspectJSecurityInterceptor`用于域对象实例安全，而 AOP Alliance`MethodSecurityInterceptor`用于服务层安全。

让我们首先考虑在 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 Security Interceptor 完全相同。实际上，这两个拦截器可以共享相同的`securityMetadataSource`，因为`SecurityMetadataSource`处理`java.lang.reflect.Method`s，而不是 AOP 库特定的类。当然，你的访问决策具有对相关 AOP 库特定调用的访问权限（即`MethodInvocation`或`JoinPoint`），因此在进行访问决策时可以考虑一系列附加条件（例如方法参数）。

接下来，你需要定义一个 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`调用这个匿名的`AspectJCallback`类。

你将需要配置 Spring 来加载该方面，并将其与`AspectJSecurityInterceptor`连接。实现这一目标的 Bean 声明如下：

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

就是这样！现在，你可以在应用程序中的任何地方创建 bean，使用你认为合适的任何方法（例如`new Person();`），并且它们将应用安全拦截器。

[基于表达式的访问控制](expression-based.html)[方法安全性](method-security.html)

