我正在使用spring-security-tiger-2.0.5。如何以編程方式將安全性添加到Spring Bean
有沒有辦法以編程方式將安全代理添加到Spring Bean?
我正在通過BeanDefinitionBuilder構造bean,並且我想添加與@Secured註釋相同的行爲。
roleName的@Secured等價物將作爲參數傳遞。
我正在使用spring-security-tiger-2.0.5。如何以編程方式將安全性添加到Spring Bean
有沒有辦法以編程方式將安全代理添加到Spring Bean?
我正在通過BeanDefinitionBuilder構造bean,並且我想添加與@Secured註釋相同的行爲。
roleName的@Secured等價物將作爲參數傳遞。
您應該查看org.springframework.aop.framework.ProxyFactory 類。與ProxyFactory裏包裹你的bean,並添加安全攔截
要以編程方式注入春季安全功能集成到現有的豆,你可能需要使用Spring Security的一個應用方面,有註冊自己的bean:我用
@Test
public void testSpringSecurity() throws Exception {
InMemoryXmlApplicationContext ctx = initSpringAndSpringSecurity();
// Creates new instance
IMyService secured = (IMyService) ctx.getAutowireCapableBeanFactory()
.initializeBean(new MyService(), "myService");
assertTrue(AopUtils.isAopProxy(secured));
fakeSecurityContext("ROLE_USER");
secured.getCustomers(); // Works: @Secured("ROLE_USER")
fakeSecurityContext("ROLE_FOO");
try {
secured.getCustomers(); // Throws AccessDenied Exception
fail("AccessDeniedException expected");
} catch (AccessDeniedException expected) {
}
}
private InMemoryXmlApplicationContext initSpringAndSpringSecurity() {
InMemoryXmlApplicationContext ctx = new InMemoryXmlApplicationContext(
"<b:bean id='authenticationManager' class='org.springframework.security.MockAuthenticationManager' /> "
+ "<b:bean id='accessDecisionManager' class='org.springframework.security.vote.UnanimousBased'><b:property name='decisionVoters'><b:list><b:bean class='org.springframework.security.vote.RoleVoter'/></b:list></b:property></b:bean>"
+ "<b:bean id='objectDefinitionSource' class='org.springframework.security.annotation.SecuredMethodDefinitionSource' /> "
+ "<b:bean id='autoproxy' class='org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator'/>"
+ "<b:bean id='methodSecurityAdvisor' class='org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor' autowire='constructor'/>"
+ "<b:bean id='securityInterceptor' class='org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor'><b:property name='authenticationManager' ref='authenticationManager' /><b:property name='accessDecisionManager' ref='accessDecisionManager' /><b:property name='objectDefinitionSource' ref='objectDefinitionSource' /></b:bean>");
return ctx;
}
一個內存中的應用程序上下文,因爲MethodDefinitionSourceAdvisor
在其文檔中聲明自動代理僅適用於ApplicationContext
。因此,如果您不爲每個服務對象使用單獨的ProxyFactoryBean
,我相信您需要自動代理的應用上下文。但是由於您使用的是@Secured
註釋,我懷疑這與自動代理相同。
fakeSecurityContext()
只是將具有給定角色的Authentication
對象設置爲SecurityContextHolder
用於測試目的。
您可以使用Spring Core的功能自行完成。假設您獲得了返回客戶列表的服務,並且當前用戶只能查看具有特定收入限制的客戶。下面的測試案例將是我們的開始:
@Test
public void testSecurity() throws Exception {
ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext(
"spring.xml");
IMyService service = (IMyService) appCtx.getBean("secured",
IMyService.class);
assertEquals(1, service.getCustomers().size());
}
這是原來的服務實現:
public class MyService implements IMyService {
public List<Customer> getCustomers() {
return Arrays.asList(new Customer(100000), new Customer(5000));
}
}
配置您的服務對象在spring.xml
並添加方法攔截:
<bean id="service" class="de.mhaller.spring.MyService"></bean>
<bean id="securityInterceptor" class="de.mhaller.spring.MyServiceInterceptor">
<property name="revenueLimit" value="50000"/>
</bean>
<bean id="secured" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="service" />
<property name="interceptorNames">
<list>
<value>securityInterceptor</value>
</list>
</property>
</bean>
的安全攔截器實施:
public class MyServiceInterceptor implements MethodInterceptor {
private int revenueLimit = 10000;
public void setRevenueLimit(int revenueLimit) {
this.revenueLimit = revenueLimit;
}
@SuppressWarnings("unchecked")
public Object invoke(MethodInvocation mi) throws Throwable {
List<Customer> filtered = new ArrayList<Customer>();
List<Customer> result = (List<Customer>) mi.proceed();
for (Customer customer : result) {
if (customer.isRevenueBelow(revenueLimit)) {
filtered.add(customer);
}
}
return filtered;
}
}
使用這種方法的優點是,您不僅可以聲明地檢查當前用戶的角色,還可以動態方式強制執行公司策略,例如,根據業務價值限制返回的對象。
嗨,謝謝你的好回答,但是我要找的東西有點不同。 我正在使用spring-security,使用基於切入點的攔截器,因此只有某些角色被允許用於給定的方法調用。 我的服務,正在以編程方式註冊到Spring,所以我不能使用ApplicationContext方法。 我不想在我自己的代理中重新實現spring-security funcionality,但要以與applicationContext中聲明的方式相同的方式使用它,但要以編程方式創建它。 TKS。 – 2009-12-25 14:53:24
感謝您的答案,但我最終得到它在一個完全程序化的方式工作。這不是一個簡單的解決方案,但我想分享它。
首先,我可能分裂的問題分成兩個部分:
1)創建我自己的豆子代理。
2)將security-roles添加到每個bean。不幸的是,我無法添加一個基於xml的切入點,因爲這些bean都是同一個類(一個通用服務)。我想要一種方式來保護每個bean,儘管它們都是同一個類。 我決定採用aopalliance使用相同的聲明(。(執行等)
對他們來說可能會感興趣,這是我如何做的:
1)用於spring's的BeanNameAutoProxyCreator類,用於創建類各地代理我手動添加:
private void addProxies(ConfigurableListableBeanFactory beanFactory, List<String> exportedServices) {
List<String> interceptorNames = findInterceptorNames(beanFactory);
BeanNameAutoProxyCreator beanPostProcessor = new BeanNameAutoProxyCreator();
beanPostProcessor.setBeanNames(exportedServices.toArray(new String[exportedServices.size()]));
beanPostProcessor.setInterceptorNames(interceptorNames.toArray(new String[interceptorNames.size()]));
beanPostProcessor.setBeanFactory(beanFactory);
beanPostProcessor.setOrder(Ordered.LOWEST_PRECEDENCE);
beanFactory.addBeanPostProcessor(beanPostProcessor);
}
@SuppressWarnings("unchecked")
private List<String> findInterceptorNames(ConfigurableListableBeanFactory beanFactory) {
List<String> interceptorNames = new ArrayList<String>();
List<? extends Advisor> list = new BeanFactoryAdvisorRetrievalHelper(beanFactory).findAdvisorBeans();
for (Advisor ad : list) {
Advice advice = ad.getAdvice();
if (advice instanceof MethodInterceptor) {
Map<String, ?> beansOfType = beanFactory.getBeansOfType(advice.getClass());
for (String name : beansOfType.keySet()) {
interceptorNames.add(name);
}
}
}
return interceptorNames;
}
2)爲確保參數化類部分中獲取:
創建spring's MethodDefinitionSource的實現。 MethodSecurityProxy在運行時使用此接口的實現者來獲取securedObjects。
@SuppressWarnings("unchecked")
@Override
public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {
if (!(object instanceof ReflectiveMethodInvocation)) {
return null;
}
ReflectiveMethodInvocation invokation = (ReflectiveMethodInvocation) object;
if (!(invokation.getThis() instanceof MyService)) {
return null;
}
MyService service = (MyService) invokation.getThis();
Map<String, String> map =getProtectedServiceMethodMap(service);
String roles = map.get(MethodKeyGenerator.generate(invokation.getMethod()));
return roles == null ? null : new ConfigAttributeDefinition(StringUtils.delimitedListToStringArray(roles, ","));
}
最棘手的部分是我的MethodDefinitionSource IMPL註冊到ApplicationContext中,使MethodSecurityInterceptor開始意識到這一點:
private void addCrudDefinitionSource() {
DelegatingMethodDefinitionSource source = (DelegatingMethodDefinitionSource) beanFactory.getBean(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE);
try {
Field field = source.getClass().getDeclaredField("methodDefinitionSources");
field.setAccessible(true);
List list = (List) field.get(source);
list.add(new MyMethodDefinitionSource());
} catch (Exception e) {
e.printStackTrace();
}
}
最後,在一個AOP風格的檢查方法的入口點我用這樣的代碼如下:
private void addToCache(){ // Ommiting parameters
PointcutExpression expression = parser.parsePointcutExpression(fullExpression);
Method[] methods = clazzToCheck.getMethods();
for (int i = 0; i < methods.length; i++) {
Method currentMethod = methods[i];
boolean matches = expression.matchesMethodExecution(currentMethod).alwaysMatches();
if (matches){
//Add to Cache
}
}
我已經看過這個類,但是不能設法以正確的方式添加安全攔截器。 1)什麼是正確的課堂? (我試過MethodSecurityInterceptor) 2)如何設置正確的ROLES? Tks。 – 2009-12-25 12:18:44
它應該是這樣的:new ProxyFactory(new YourBean())。addAdvice(new MethodSecurityInterceptor());並且YourBean的方法必須使用@Secured(「ROLE_ADMIN」)註釋 – 2009-12-25 15:06:03
好的,但是我的bean不能用@Secured註釋,因爲roleName是作爲參數傳遞的(有時是「ADMIN」,有時是「USER」等。 ) tks。 – 2009-12-26 16:05:04