问题
我想创建一个类,通过注释添加自定义方法以用于spring安全表达式语言,以进行基于方法的授权。
例如,我想创建一个像'customMethodReturningBoolean'这样的自定义方法,以某种方式使用:
@PreAuthorize("customMethodReturningBoolean()")
public void myMethodToSecure() {
// whatever
}
我的问题是这个。如果可能,我应该创建自定义方法的子类,我将如何在spring xml配置文件中配置它,并且有人给我一个以这种方式使用的自定义方法的示例?
#1 热门回答(152 赞)
所提到的技术都不会再起作用了。好像Spring已经花了很大力气来阻止用户覆盖SecurityExpressionRoot。
编辑11/19/14设置Spring使用安全注释:
...
像这样创建一个bean:
@Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(String key) {
return true;
}
}
然后在你的jsp中做这样的事情:
或者注释方法:
@PreAuthorize("@mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }
请记住:如果你使用的是Spring,你必须通过扩展类,重写方法,实现接口等来解决问题......那么你可能做错了什么。它是所有注释和xml,这就是为什么我们如此喜欢Spring而不是(旧版本的)EJB。
此外,你可以在your@PreAuthorize注释中使用Spring Expression Language来访问当前身份验证以及方法参数。
例如:
@Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(Authentication authentication, String foo) { ... }
}
然后更新你的@PreAuthorize以匹配新方法签名:
@PreAuthorize("@mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }
#2 热门回答(32 赞)
你需要子类化两个类。
首先,设置一个新方法表达式处理程序
myMethodSecurityExpressionHandler将是DefaultMethodSecurityExpressionHandler的子类,覆盖createEvaluationContext(),在MethodSecurityEvaluationContext中设置子类MethodSecurityExpressionRoot。
例如:
@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
root.setTrustResolver(trustResolver);
root.setPermissionEvaluator(permissionEvaluator);
root.setRoleHierarchy(roleHierarchy);
ctx.setRootObject(root);
return ctx;
}
#3 热门回答(14 赞)
Thanksericacm,但由于以下几个原因它不起作用:
DefaultMethodSecurityExpressionHandler的属性是私有的(反射可见性kludges不合需要)
至少在我的Eclipse中,我无法解析MethodSecurityEvaluationContext对象
不同之处在于我们调用现有的createEvaluationContext方法,然后添加我们的自定义根对象。最后我刚刚返回anStandardEvaluationContextobject类型,因为MethodSecurityEvaluationContext无法在编译器中解析(它们都来自同一个接口)。这是我现在生产的代码。
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
// parent constructor
public CustomMethodSecurityExpressionHandler() {
super();
}
/**
* Custom override to use {@link CustomSecurityExpressionRoot}
*
* Uses a {@link MethodSecurityEvaluationContext} as the EvaluationContext implementation and
* configures it with a {@link MethodSecurityExpressionRoot} instance as the expression root object.
*/
@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
// due to private methods, call original method, then override it's root with ours
StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
return ctx;
}
}
这将通过扩展SecurityExpressionRoot替换默认根。在这里,我将hasRole重命名为hasEntitlement:
public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {
// parent constructor
public CustomSecurityExpressionRoot(Authentication a) {
super(a);
}
/**
* Pass through to hasRole preserving Entitlement method naming convention
* @param expression
* @return boolean
*/
public boolean hasEntitlement(String expression) {
return hasRole(expression);
}
}
最后更新securityContext.xml(并确保它从applcationContext.xml引用):
jsr250-annotations="disabled"
secured-annotations="disabled"
pre-post-annotations="enabled">
注意:@Secured注释不会接受此覆盖,因为它通过不同的验证处理程序运行。所以,在上面的xml我禁用它们以防止以后混淆。