有没有办法在@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(“”)value属性中可用的对象集合中。
1)首先,你必须重新实现MethodSecurityExpressionRoot,其中包含额外的方法特定功能。最初的Spring Security实现是程序包私有的,因此不能仅对其进行扩展。我建议检查给定类的源代码。
MethodSecurityExpressionRoot
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将使用上述定义的custom CustomMethodSecurityExpressionRoot。
MethodSecurityExpressionHandler
custom 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
@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()返回所有者的用户名的方法。
isAdmin()
getOwner()
由于@PreAuthorize评估SpEl表达式,最简单的方法就是指向一个bean:
@PreAuthorize("@mySecurityService.someFunction()")
MySecurityService.someFunction应该具有返回类型boolean。
MySecurityService.someFunction
boolean
authentication如果你想传递Authentication-object,Spring-security将自动提供一个名为的变量。你还可以使用任何有效的SpEl表达式来访问传递给安全方法的任何参数,评估正则表达式,调用静态方法等。例如:
authentication
Authentication-object
Spring-securit
@PreAuthorize("@mySecurityService.someFunction(authentication, #someParam)")