我有服务接口(interface)
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
的服务接口(interface)和扩展 CompoundServiceImpl
的自己的服务类。
我现在想向 CompoundService
中的方法添加基本的安全性 UISNG 注释。据我了解,我必须将它们添加到接口(interface)中,而不是实际的实现中。由于用户可以针对 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)实现的接口(interface),表达式 this.compoundClass
实际上会正确评估吗?
最后但并非最不重要的一点是我如何实际测试它?*
* 我实际上并不是在创建一个完成的应用程序,而是创建一些可配置的应用程序,例如用于特定类型的数据库搜索的框架。这意味着大多数授权和身份验证内容必须来自实现者。
最佳答案
- 单元测试
参见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>
没有它们它也能工作(但是没有创建抽象测试类)
root.this
这实际上是正确的做法
问题是您无法使用类参数的 getSimpleName() 。如需深入讨论,请参阅 http://forum.springsource.org/showthread.php?98570-Getting-Payload-Classname-in-Header-Enricher-via-SpEL
那里显示的解决方法对我没有多大帮助。所以我想出了这个非常简单的解决方案:
只需将字符串属性StringcompoundClassSimpleName
添加到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);
}
}
关于spring-security - @预授权: reference property in implementing class,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15001653/