安全对象(Secure Object)的实现
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springboot.io - Spring Boot中文社区 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
本节介绍Spring Security如何处理安全对象的实现。
AOP联盟(MethodInvocation)安全拦截器
在 Spring Security 2.0之前,保护 MethodInvocation
实例需要大量的配置。现在,方法安全的推荐方法是使用 命名空间配置。这样一来,方法安全基础设施Bean就会自动为你配置,所以你不需要知道实现类。我们在这里只提供了对所涉及的类的一个快速概述。
方法的安全性是通过使用 MethodSecurityInterceptor
来执行的,它可以保护 MethodInvocation
实例。根据配置方式的不同,拦截器可能是特定于单个Bean的,也可能是多个Bean之间共享的。拦截器使用 MethodSecurityMetadataSource
实例来获取适用于特定方法调用的配置属性。 MapBasedMethodSecurityMetadataSource
用于存储由方法名(可以通配符)作为key的配置属性,当属性在应用上下文中使用 <intercept-methods>
或 <protect-point>
元素定义时,将在内部使用。其他实现被用来处理基于注解的配置。
明确的 MethodSecurityInterceptor Configuration
你可以直接在你的应用上下文中配置一个 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) Security 拦截器
AspectJ的安全拦截器与上一节讨论的AOP联盟的安全拦截器非常相似。我们在本节中只讨论两者的区别。
AspectJ的拦截器被命名为 AspectJSecurityInterceptor
。与AOP联盟的安全拦截器不同,AOP联盟的安全拦截器是依靠 Spring application context 通过代理来织入安全拦截器的,而 AspectJSecurityInterceptor
是通过 AspectJ
编译器织入的。在同一个应用程序中使用两种类型的安全拦截器并不罕见,AspectJSecurityInterceptor
用于域对象(domain object)实例安全,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
),并且在做访问决策时可以考虑一系列额外的标准(如方法参数)。
接下来,你需要定义一个AspectJ 切面,如下面的例子所示。
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();
语句只在 round()
体中有特殊意义。当 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();
),并且它们有 security 拦截器。