@PreAuthorize:实现类的引用属性

时间:2013-02-21 11:56:05

标签: spring-security spring-el

我有服务界面

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中为我的方法添加基本的安全性uisng注释。据我所知,我必须在接口中添加它们而不是实际的实现。由于用户可以为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)实现的界面,所以exression this.compoundClass实际上会正确评估吗?

最后但并非最不重要的是我怎样才能真正测试这个?*

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

1 个答案:

答案 0 :(得分:2)

  1. 单元测试
  2. 请参阅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. root.this

    2. 这实际上是正确的方法

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

      那里显示的变通方法对我没什么帮助。所以我提出了这个非常简单的解决方案:

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

      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);
           }
      }