2009-12-24 97 views

回答

0

您應該查看org.springframework.aop.framework.ProxyFactory 類。與ProxyFactory裏包裹你的bean,並添加安全攔截

+0

我已經看過這個類,但是不能設法以正確的方式添加安全攔截器。 1)什麼是正確的課堂? (我試過MethodSecurityInterceptor) 2)如何設置正確的ROLES? Tks。 – 2009-12-25 12:18:44

+0

它應該是這樣的:new ProxyFactory(new YourBean())。addAdvice(new MethodSecurityInterceptor());並且YourBean的方法必須使用@Secured(「ROLE_ADMIN」)註釋 – 2009-12-25 15:06:03

+0

好的,但是我的bean不能用@Secured註釋,因爲roleName是作爲參數傳遞的(有時是「ADMIN」,有時是「USER」等。 ) tks。 – 2009-12-26 16:05:04

1

要以編程方式注入春季安全功能集成到現有的豆,你可能需要使用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; 
    } 

} 

使用這種方法的優點是,您不僅可以聲明地檢查當前用戶的角色,還可以動態方式強制執行公司策略,例如,根據業務價值限制返回的對象。

+0

嗨,謝謝你的好回答,但是我要找的東西有點不同。 我正在使用spring-security,使用基於切入點的攔截器,因此只有某些角色被允許用於給定的方法調用。 我的服務,正在以編程方式註冊到Spring,所以我不能使用ApplicationContext方法。 我不想在我自己的代理中重新實現spring-security funcionality,但要以與applicationContext中聲明的方式相同的方式使用它,但要以編程方式創建它。 TKS。 – 2009-12-25 14:53:24

0

感謝您的答案,但我最終得到它在一個完全程序化的方式工作。這不是一個簡單的解決方案,但我想分享它。

首先,我可能分裂的問題分成兩個部分:

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; 
} 
  • 其中exportedServices代表我想代理豆
  • BeanFactory的實例可以使用我自己的實現的BeanFactoryPostProcessor

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 
    } 
    } 
相關問題