我正在使用spring-security-tiger-2.0.5。

有没有办法以编程方式将安全代理添加到Spring Bean?

我正在通过BeanDefinitionBuilder构造该bean,我想添加与@Secured批注相同的行为。

RoleName的@Secured等效项将作为参数传递。

最佳答案

要将Spring Security功能以编程方式注入到现有的bean中,您可能需要使用Spring Security applicaton上下文并在其中注册您的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;
    }

}


使用这种方法的优势在于,您不仅可以声明性地检查当前用户的角色,而且还可以以动态方式(例如,动态地)执行公司政策。根据业务价值限制返回的对象。

07-24 09:49
查看更多