2

我有服务接口

public interface CompoundService<T extends Compound> {

    T getById(final Long id);

    //...
}

和抽象实现

public abstract class CompoundServiceImpl<T extends Compound>
        implements CompoundService<T> {

    //...   

    private Class<T> compoundClass;

    //...
}

的每个实现都Compound需要它自己的扩展服务接口CompoundService和它自己的扩展服务类CompoundServiceImpl

我现在想在CompoundService. 据我了解,我必须将它们添加到接口而不是实际实现中。由于用户可以针对不同的实现具有不同的角色Compound,因此我必须考虑到这一点。意思是@PreAuthorize我想获得Compound实现的名称,例如。compoundClass.getSimpleName(). 所以我得到类似的东西:

public interface CompoundService<T extends Compound> {

    @PreAuthorize("hasRole('read_' + #root.this.compoundClass.getSimpleName())")
    T getById(final Long id);

    //...
}

这基本上就是这里提到的:

https://jira.springsource.org/browse/SEC-1640

但是没有示例,我并没有真正得到解决方案。那我应该用this吗?或如上#root.this

我的第二个问题是,因为这是在一个将由代理(来自 spring)实现的接口中,所以 experession 是否会this.compoundClass正确评估?

最后但并非最不重要的一点是,我该如何实际测试呢?*

* 我实际上并没有创建一个完成的应用程序,而是一些可配置的东西,比如用于特定类型数据库搜索的框架。这意味着大多数授权和身份验证的东西必须来自实施者。

4

1 回答 1

2
  1. 单元测试

http://www.lancegleason.com/blog/2009/12/07/unit-testing-spring-security-with-annotations

由于这是一个旧教程,您可能需要更改引用的架构版本。但更重要的是,此处显示的 SecurityContext.xml 配置不适用于 Spring Security 3。有关正确配置,请参阅Spring Security - multiple authentication-providers

我不需要提到的依赖项:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core-tiger</artifactId>
</dependency>

它在没有它们的情况下工作(但是没有创建抽象测试类)

  1. 根.this

这实际上是正确的方法

问题是您不能使用类参数的 getSimpleName() 。有关深入讨论,请参阅http://forum.springsource.org/showthread.php?98570-Getting-Payload-Classname-in-Header-Enricher-via-SpEL

那里显示的解决方法对我没有多大帮助。所以我想出了这个非常简单的解决方案:

只需添加字符串属性并将String compoundClassSimpleNameCompoundServiceImpl设置在构造函数中(由子类调用):

Public abstract class CompoundServiceImpl<T extends Compound>
    implements CompoundService<T> {
    
    private String compoundClassSimpleName;

    //...
    
    public ChemicalCompoundServiceImpl(Class<T> compoundClass) {
        this.compoundClass = compoundClass;
        this.compoundClassSimpleName = compoundClass.getSimpleName();
    }
    
    //...
    
    public String getCompoundClassSimpleName(){
        return compoundClassSimpleName;
    }   
}

和她实现上述抽象服务的服务:

public class TestCompoundServiceImpl extends CompoundServiceImpl<TestCompound>
        implements TestCompoundService {

    //...   

    public TestCompoundServiceImpl() {
        super(TestCompound.class);
    }
    
    //...   
    
}

最后@PreAuthorize注释用法:

public interface CompoundService<T extends Compound> {

    @PreAuthorize("hasRole('read_' + #root.this.getCompoundClassSimpleName())")
    public T getById(final Long id);
}

对于上面的示例,表达式将评估为名为“read_TestCompound”的角色。

完毕!

通常解决方案非常简单,但要到达那里有一个 PITA ......

编辑:

为了完整性测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
        "classpath:ApplicationContext.xml",
        "classpath:SecurityContext.xml"
        })
public class CompoundServiceSecurityTest {

    @Autowired
    @Qualifier("testCompoundService")
    private TestCompoundService testCompoundService;

    public CompoundServiceSecurityTest() {
    }
    

    @Before
    public void setUp() {
        SecurityContextHolder.getContext().setAuthentication(
            new UsernamePasswordAuthenticationToken("user_test", "pass1"));
    }

     @Test
     public void testGetById() {
        System.out.println("getById");
        Long id = 1000L;
        TestCompound expResult = new TestCompound(id, "Test Compound");
        TestCompound result = testCompoundService.getById(id);
        assertEquals(expResult, result);
     }
}
于 2013-02-21T14:07:53.263 回答