1) 首先你必须重新实现MethodSecurityExpressionRoot,它包含额外的特定于方法的功能。最初的 Spring Security 实现是包私有的,因此不能仅仅扩展它。我建议检查给定类的源代码。
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
// copy everything from the original Spring Security MethodSecurityExpressionRoot
// add your custom methods
public boolean isAdmin() {
// do whatever you need to do, e.g. delegate to other components
// hint: you can here directly access Authentication object
// via inherited authentication field
}
public boolean isOwner(Long id) {
// do whatever you need to do, e.g. delegate to other components
}
}
2) 接下来您必须实现自定义MethodSecurityExpressionHandler,它将使用上面定义的CustomMethodSecurityExpressionRoot。
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
@Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
}
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
3) 在您的上下文中定义表达式处理程序 bean,例如通过 XML,您可以按如下方式进行操作
<bean id="methodSecurityExpressionHandler"
class="my.package.CustomMethodSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy" />
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
4) 注册上面定义的处理程序
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>
5) 然后只需在 @PreAuthorize 和/或 @PostAuthorize 注释中使用定义的表达式
@PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) {
// do whatever needed
}
还有一件事。使用方法级别的安全性来保护控制器方法并不是很常见,而是使用业务逻辑来保护方法(也就是您的服务层方法)。然后你可以使用类似下面的东西。
public interface GameService {
// rest omitted
@PreAuthorize("principal.admin or #game.owner = principal.username")
public void delete(@P("game") Game game);
}
但请记住,这只是一个示例。它期望实际的主体具有isAdmin() 方法,并且游戏具有返回所有者用户名的getOwner() 方法。