如何在 Spring Security @PreAuthorize/@PostAuthorize 注解中使用自定义表达式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26747452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:11:28  来源:igfitidea点击:

How to use custom expressions in Spring Security @PreAuthorize/@PostAuthorize annotations

springsecurityspring-security

提问by Jazzepi

Is there a way to create more expressive statements in @Preauthorize blocks? Here's an example of something I find myself repeating, because the @Preauthorize is not terribly smart out of the box.

有没有办法在 @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.");
    }
}

What I would prefer is something like.

我更喜欢的是类似的东西。

@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));
}

Part of the problem is that I need to make a query to the database to fetch some of this stuff to verify permissions, such as querying the database to get a copy of the game, and then comparing the owner of the game to the person making the request. I'm not really sure how all of that operates within the context of a @Preauthorize annotation processor, or how I add things to the collection of objects made available in the @Preauthorize("") value attribute.

部分问题是我需要查询数据库来获取其中一些东西来验证权限,例如查询数据库以获取游戏的副本,然后将游戏所有者与制作人进行比较请求。我不确定所有这些如何在 @Preauthorize 注释处理器的上下文中运行,或者我如何将内容添加到 @Preauthorize("") 值属性中提供的对象集合中。

采纳答案by pgiecek

1) First you have to reimplement MethodSecurityExpressionRootwhich contains extra method-specific functionality. The original Spring Security implementation is package private and hence it is not possible to just extend it. I suggest checking the source code for the given class.

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) Next you have to implement custom MethodSecurityExpressionHandlerthat will use the above defined CustomMethodSecurityExpressionRoot.

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) Define expression handler bean in your context, e.g. via XML you can do it as follows

3)在您的上下文中定义表达式处理程序bean,例如通过XML,您可以按如下方式进行

<bean id="methodSecurityExpressionHandler"
    class="my.package.CustomMethodSecurityExpressionHandler">
    <property name="roleHierarchy" ref="roleHierarchy" />
    <property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>

4) Register the above defined handler

4) 注册上面定义的处理程序

<security:global-method-security pre-post-annotations="enabled">
    <security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>

5) Then just use the defined expressions in your @PreAuthorizeand/or @PostAuthorizeannotations

5)然后只需在您的@PreAuthorize和/或@PostAuthorize注释中使用定义的表达式

@PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(@PathVariable int id, @ModelAttribute currentGame) {
    // do whatever needed
}

And one more thing. It is not very common to use method level security to secure controller methods but rather to secure methods with business logic (a.k.a. your service layer methods). Then you could use something like the below.

还有一件事情。使用方法级别的安全性来保护控制器方法的安全性并不常见,而是使用业务逻辑(也就是您的服务层方法)来保护方法。然后你可以使用类似下面的东西。

public interface GameService {

    // rest omitted

    @PreAuthorize("principal.admin or #game.owner = principal.username")
    public void delete(@P("game") Game game);
}

But keep in mind that this is just an example. It expects that the actual principal has isAdmin()method and that the game has getOwner()method returning username of the owner.

但请记住,这只是一个示例。它期望实际主体具有isAdmin()方法,并且游戏具有getOwner()返回所有者用户名的方法。

回答by gogstad

Since @PreAuthorizeevaluates SpEl-expressions, the easiest way is just to point to a bean:

由于@PreAuthorize评估SpEl 表达式,最简单的方法就是指向一个 bean:

    @PreAuthorize("@mySecurityService.someFunction()")

MySecurityService.someFunctionshould have return type boolean.

MySecurityService.someFunction应该有返回类型boolean

Spring-security will automatically provide a variable named authenticationif you want to pass the Authentication-object. You can also use any valid SpEl-expressions to access any arguments passed to your secure method, evaluate regular expressions, call static methods, etc. E.g:

authentication如果您想传递Authentication-object,Spring-security 将自动提供一个名为的变量。您还可以使用任何有效的 SpEl 表达式来访问传递给您的安全方法的任何参数、评估正则表达式、调用静态方法等。例如:

    @PreAuthorize("@mySecurityService.someFunction(authentication, #someParam)")

回答by holmis83

You could write your annotation something like:

您可以编写如下注释:

@PreAuthorize("hasRole('ROLE_ADMIN') and hasPermission(#id, 'Game', 'DELETE')")

To get the hasPermission part working you need to implement PermissionEvaluatorinterface.

要使 hasPermission 部分工作,您需要实现PermissionEvaluator接口。

Then define an expression handler bean:

然后定义一个表达式处理程序 bean:

@Autowired
private PermissionEvaluator permissionEvaluator;

@Bean
public DefaultMethodSecurityExpressionHandler expressionHandler()
{
    DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
    handler.setPermissionEvaluator(permissionEvaluator);
    return handler;
}

And inject in your security config:

并注入您的安全配置:

<global-method-security pre-post-annotations="enabled">
  <expression-handler ref="expressionHandler" />
</global-method-security>