Spring 安全自定义 PermissionEvaluator

Spring Security custom PermissionEvaluator

我正在使用 JWT 令牌授权。 我正在尝试限制对某些 REST api 端点的访问。我希望它像这样工作:下面的代码应该只在 @PathVariable

中给出的经过身份验证的用户 id == id 时执行
@PreAuthorize("hasAnyAuthority('ADMIN', 'USER')")
@GetMapping(value = "orders/{id}", produces = "application/json")
public EntityModel<Order> getOrders(@PathVariable Long id) { ... )

这些文章或多或少描述了我试图完成的事情:similar post, article

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

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

要触发 PermissionEvaluator ,您必须在 @PreAuthorize 中使用 hasPermission()hasPermission() 有 2 个版本,它们是:

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

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

/** targetDomainObject = 'foo',  permission = 'bar' **/

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

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

在这两种情况下,Authentication 参数是从 SecurityContext.

获取的 Authentication 令牌

需要注意的一点是,在配置@PreAuthorize("hasPermission()")时,可以使用spring数据中的#foo@P@Param来指定参数在受保护的方法中将用于调用 PermissionEvaluator 。有关详细信息,请参阅 this

在你的情况下,你可以这样做:

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

}

并且 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
            }
    }
}

请注意,它假定您还将 Authentication 令牌自定义为 MyAuthentication,其中包括用户 ID。它还回答了您的第二个问题,即您可以将身份验证过程自定义为 return 自定义 Authentication 令牌,您可以在加载用户记录进行身份验证后将 userId 设置到其中。这样,userId 将存储在 MyAuthentication 中,您无需在 PermissionEvaluator.

中再次查询它

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

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

}