有没有办法在@Preauthorize块中创建更具表现力的语句?这是我发现自己重复的一个例子,因为@Preauthorize不是非常聪明的开箱即用。
@RequestMapping(value = "{id}", method = RequestMethod.DELETE) public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser) { Game currentGame = gameService.findById(id); if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) { gameService.delete(gameService.findById(id)); } else { throw new SecurityException("Only an admin, or an owner can delete a game."); } }
我喜欢什么就像
@RequestMapping(value = "{id}", method = RequestMethod.DELETE) @Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id) public void deleteGame(@PathVariable int id, @ModelAttribute User authenticatingUser, @ModelAttribute currentGame ) { //I'm not sure how to add this either :( gameService.delete(gameService.findById(id)); }
部分问题是,我需要查询数据库以获取一些这样的东西来验证权限,例如查询数据库以获取游戏的副本,然后将游戏的所有者与制作人员进行比较请求。我不确定在@Preauthorize注解处理器的上下文中如何运行,或者如何将事物添加到@Preauthorize(“”)值属性中提供的对象集合中。
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 }
还有一件事。使用方法级别的安全性来保护控制器方法,而不是使用业务逻辑(a.k.a.您的服务层方法)来保护方法并不常见。那么你可以使用下面的东西。
public interface GameService { // rest omitted @PreAuthorize("principal.admin or #game.owner = principal.username") public void delete(@P("game") Game game); }
但请记住,这只是一个例子。它期望实际的主体是isAdmin()方法,游戏中的getOwner()方法返回所有者的用户名。