此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.3! |
安全对象实现
本节介绍 Spring Security 如何处理 Secure Object 实现。
AOP 联盟 (MethodInvocation) 安全拦截器
在 Spring Security 2.0 之前,保护MethodInvocation
实例需要大量的样板配置。
现在,方法安全性的推荐方法是使用命名空间配置。
这样,系统会自动为您配置方法安全基础结构 bean,因此您无需了解实现类。
我们只提供此处涉及的类的快速概述。
方法安全性通过使用MethodSecurityInterceptor
,它保护MethodInvocation
实例。
根据配置方法,拦截器可能特定于单个 bean 或在多个 bean 之间共享。
拦截器使用MethodSecurityMetadataSource
实例以获取适用于特定方法调用的配置属性。MapBasedMethodSecurityMetadataSource
用于存储以方法名称(可以是通配符)为键的配置属性,当在应用程序上下文中使用<intercept-methods>
或<protect-point>
元素。
其他实现用于处理基于 Comments 的配置。
显式 MethodSecurityInterceptor 配置
您可以配置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 (JoinPoint) 安全拦截器
AspectJ 安全拦截器与上一节中讨论的 AOP Alliance 安全拦截器非常相似。 我们仅在本节中讨论差异。
AspectJ 拦截器被命名为AspectJSecurityInterceptor
.
与 AOP Alliance 安全拦截器不同,AOP Alliance 安全拦截器依赖于 Spring 应用程序上下文通过代理来编织安全拦截器,AspectJSecurityInterceptor
是通过 AspectJ 编译器编织的。
在同一个应用程序中使用这两种类型的安全拦截器并不少见,其中AspectJSecurityInterceptor
用于域对象实例安全性和 AOP 联盟MethodSecurityInterceptor
用于服务层安全性。
我们首先考虑AspectJSecurityInterceptor
在 Spring 应用程序上下文中配置:
<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>
两个拦截器可以共享相同的securityMetadataSource
,作为SecurityMetadataSource
适用于java.lang.reflect.Method
实例,而不是特定于 AOP 库的类。
您的访问决策可以访问相关的特定于 AOP 库的调用 (MethodInvocation
或JoinPoint
),并且可以在做出访问决策时考虑一系列附加条件(例如方法参数)。
接下来,您需要定义一个 AspectJaspect
,如下例所示:
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 preceding example, the security interceptor is 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.
You need to configure Spring to load the aspect and wire it with the AspectJSecurityInterceptor
.
The following example shows a bean declaration that achieves this:
<bean id="domainObjectInstanceSecurityAspect"
class="security.samples.aspectj.DomainObjectInstanceSecurityAspect"
factory-method="aspectOf">
<property name="securityInterceptor" ref="bankManagerSecurity"/>
</bean>
Now you can create your beans from anywhere within your application, using whatever means you think fit (e.g. new Person();
), and they have the security interceptor applied.