此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.3spring-doc.cadn.net.cn

安全对象实现

AOP 联盟 (MethodInvocation) 安全拦截器

在 Spring Security 2.0 之前,保护MethodInvocation需要相当多的样板配置。 现在,方法安全性的推荐方法是使用命名空间配置。 这样,系统会自动为您配置方法安全性基础结构 bean,因此您实际上不需要了解实现类。 我们只提供此处涉及的类的快速概述。spring-doc.cadn.net.cn

方法安全性是使用MethodSecurityInterceptor,它保护MethodInvocations. 根据配置方法,拦截器可能特定于单个 bean 或在多个 bean 之间共享。 拦截器使用MethodSecurityMetadataSource实例以获取适用于特定方法调用的配置属性。MapBasedMethodSecurityMetadataSource用于存储以方法名称(可以是通配符)为键的配置属性,当在应用程序上下文中使用<intercept-methods><protect-point>元素。 其他 implementations 将用于处理基于 annotation 的配置。spring-doc.cadn.net.cn

显式 MethodSecurityInterceptor 配置

当然,您可以配置MethodSecurityInterceptor直接在你的应用程序上下文中与 Spring AOP 的代理机制之一一起使用:spring-doc.cadn.net.cn

<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 (JoinPoint) 安全拦截器

AspectJ 安全拦截器与上一节中讨论的 AOP Alliance 安全拦截器非常相似。 事实上,我们只会在本节中讨论差异。spring-doc.cadn.net.cn

AspectJ 拦截器被命名为AspectJSecurityInterceptor. 与 AOP Alliance 安全拦截器不同,AOP Alliance 安全拦截器依赖于 Spring 应用程序上下文通过代理编织安全拦截器,AspectJSecurityInterceptor是通过 AspectJ 编译器编织的。 在同一个应用程序中使用这两种类型的安全拦截器并不少见,其中AspectJSecurityInterceptor用于域对象实例安全性和 AOP 联盟MethodSecurityInterceptor用于服务层安全性。spring-doc.cadn.net.cn

我们首先考虑一下AspectJSecurityInterceptor在 Spring 应用程序上下文中配置:spring-doc.cadn.net.cn

<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,作为SecurityMetadataSource适用于java.lang.reflect.Methods,而不是特定于 AOP 库的类。 当然,您的访问决策可以访问相关的特定于 AOP 库的调用(即MethodInvocationJoinPoint),因此在做出访问决策(例如方法参数)时可以考虑一系列添加条件。spring-doc.cadn.net.cn

接下来,您需要定义一个 AspectJaspect. 例如:spring-doc.cadn.net.cn

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");
		}
	}
}

In the above example, the security interceptor will be applied to every instance of PersistableEntity, which is an abstract class not shown (you can use any other class or pointcut expression you like). For those curious, AspectJCallback is needed because the proceed(); statement has special meaning only within an around() body. The AspectJSecurityInterceptor calls this anonymous AspectJCallback class when it wants the target object to continue.spring-doc.cadn.net.cn

You will need to configure Spring to load the aspect and wire it with the AspectJSecurityInterceptor. A bean declaration which achieves this is shown below:spring-doc.cadn.net.cn

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

That’s it! Now you can create your beans from anywhere within your application, using whatever means you think fit (e.g. new Person();) and they will have the security interceptor applied.spring-doc.cadn.net.cn