【问题标题】:Spring Security custom PermissionEvaluatorSpring Security 自定义 PermissionEvaluator
【发布时间】:2020-09-24 18:39:30
【问题描述】:

我正在使用 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 postarticle

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

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

【问题讨论】:

    标签: spring-boot spring-security jwt-auth


    【解决方案1】:

    要触发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来指定protected方法中的哪个参数将用于调用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。它还回答了您的第二个问题,即您可以自定义身份验证过程以返回自定义的Authentication 令牌,您可以在加载用户记录以进行身份​​验证后将 userId 设置到其中。这样userId就会被存储在MyAuthentication里面,你不需要在PermissionEvaluator里​​面再次查询。

    另外,你也可以考虑直接在@PreAuthorize中表达授权逻辑,这样简单的情况不用hasPermission()

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

    【讨论】:

    • 嘿,你能帮我定制身份验证令牌吗? (我制作了没有它的版本,当然工作正常)。我返回的是:UsernamePasswordAuthenticationToken(user, null, grantedAuths); 我必须扩展它吗?请至少给我一些例子,我试图扩展 User 类,希望我以后能够通过authentication.getPrincipal.getEmail() 访问它的字段,但它似乎不起作用。
    猜你喜欢
    • 2015-03-24
    • 2023-03-04
    • 2019-02-05
    • 2016-09-04
    • 1970-01-01
    • 1970-01-01
    • 2018-01-13
    • 2015-12-15
    • 2016-04-23
    相关资源
    最近更新 更多