Spring Security 自定义 PermissionEvaluator

Nic*_*e11 6 spring-security jwt spring-boot

我正在使用 JWT 令牌授权。我正在尝试限制对某些 REST api 端点的访问。我希望它像这样工作:下面的代码应该仅在经过身份验证的用户 id == 中给出的 id 时执行@PathVariable

@PreAuthorize("hasAnyAuthority('ADMIN', 'USER')")
@GetMapping(value = "orders/{id}", produces = "application/json")
public EntityModel<Order> getOrders(@PathVariable Long id) { ... )
Run Code Online (Sandbox Code Playgroud)

这些文章或多或少描述了我想要完成的事情:类似的帖子文章

我不明白CustomPermissionEvaluator implements PermissionEvaluator在我的情况下第二个链接应该是什么样子。如果有人能给我一些提示,我将不胜感激。

我的第二个问题是,我以后能做的就是获取 username authentication.getName(),然后我必须使用UserRepositoryto findUserByUsername(String username)。这样做正常吗?这是额外的数据库查询。我想知道是否可以以某种方式将 userID 添加到令牌(因为返回的函数UsernamePasswordAuthenticationToken已经通过用户名获取用户)。

Ken*_*han 9

要触发PermissionEvaluator,您必须使用hasPermission()in @PreAuthorize。有 2 个版本hasPermission()

(1)@PreAuthorize("hasPermission('foo' ,'bar')") 将调用

boolean hasPermission(Authentication authentication, Object targetDomainObject,Object permission);

/** targetDomainObject = 'foo',  permission = 'bar' **/
Run Code Online (Sandbox Code Playgroud)

(2) @PreAuthorize("hasPermission('foo' ,'bar','baz')")将调用

boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);
/** targetId = 'foo' , targetType = 'bar' , permission = 'baz' **/
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,Authentication参数都是AuthenticationSecurityContext.

需要注意的一件事是,在配置时 @PreAuthorize("hasPermission()"),您可以使用spring data 中的 或#foo来指定 protected 方法中的哪个参数将用于调用. 请参阅了解更多详细信息。@P@ParamPermissionEvaluator

对于你的情况,你可以这样做:

@PreAuthorize("hasPermission('#id', 'getOrder')")
public EntityModel<Order> getOrders(@PathVariable Long id) {

}
Run Code Online (Sandbox Code Playgroud)

看起来PermissionEvaluator像:

public class MyPermissionEvaluator implements PermissionEvaluator {


    @Override
    public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {

            MyAuthentication myAuth = (MyAuthentication) auth;
            Long targetId =  (Long) id;
            String permssionStr = (String) permission;

            if(permssionStr.equals("getOrder")){
                return myAuth.getUserId().equals(targetId);
            }else if(permssionStr.equals("xxxx"){
                //other permission checking
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,它假设您还自定义了包含 userID 的Authentication令牌MyAuthentication。它还回答了您的第二个问题,即您可以自定义身份验证过程以返回自定义Authentication令牌,您可以在加载用户记录进行身份验证后将 userId 设置到其中。这样,userId就会保存在里面MyAuthentication,不需要再在里面查询了PermissionEvaluator

或者,对于这种简单的情况,您也可以考虑直接在 中 表达授权逻辑,而@PreAuthorize无需使用:hasPermission()

@PreAuthorize("#id == authentication.userId")
public EntityModel<Order> getOrders(@PathVariable Long id) {

}
Run Code Online (Sandbox Code Playgroud)