权限代码和名称的 Spring Security 表达式

Posted

技术标签:

【中文标题】权限代码和名称的 Spring Security 表达式【英文标题】:Spring Security expressions for permission code and name 【发布时间】:2017-02-11 07:47:49 【问题描述】:

在我的 Spring Security 项目中,我有一个使用 @PreAuthorize 注释保护的以下方法:

@PreAuthorize("hasAnyAuthority('PERMISSION_UPDATE_OWN_DECISION', 'PERMISSION_UPDATE_ANY_DECISION')")
@RequestMapping(value = "/decisionId/update", method = RequestMethod.POST)
public DecisionResponse updateDecision(@PathVariable @NotNull @DecimalMin("0") Long decisionId, @Valid @RequestBody UpdateDecisionRequest decisionRequest) 
    ....

我还将 Spring OAuth2 与 JWT 令牌一起使用,并将所有权限存储在此令牌中。现在,我使用了很长的权限名称,例如 PERMISSION_UPDATE_OWN_DECISION,并希望替换它们以显着减少 JWT 令牌大小(权限部分)。

其中一个想法是引入权限代码,例如:

1, PERMISSION_UPDATE_OWN_DECISION
2, PERMISSION_UPDATE_ANY_DECISION

其中1 是权限代码,PERMISSION_UPDATE_OWN_DECISION 是权限名称。

但我不想直接使用这些代码,因为它会降低我的代码的可读性,例如:

@PreAuthorize("hasAnyAuthority('1', '2')")

我想使用一些允许我根据真实权限名称检索此代码的东西,例如:

@PreAuthorize("hasAnyAuthority(getCode('PERMISSION_UPDATE_OWN_DECISION')), getCode('PERMISSION_UPDATE_ANY_DECISION'))")

如何使用 Spring Security 和 Spring Security Expressions 正确实现这一点?可能有一些预先构建的方法来实现这一点?

【问题讨论】:

【参考方案1】:

首先,您需要继承 MethodSecurityExpressionRoot 并使用您的逻辑创建自定义类。

public class CustomMethodSecurityExpressionRoot extends MethodSecurityExpressionRoot 
    public boolean hasAnyAuthorityWithCodes(String... codes) 
         String[] ids = // Your custom Logic to get Ids from Code

         return hasAnyAuthority(ids);
    


然后继承 DefaultMethodSecurityExpressionHandler 并覆盖它的 createEvaluationContext 方法。

@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) 
    MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
    MethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(auth);
    root.setTrustResolver(trustResolver);
    root.setPermissionEvaluator(permissionEvaluator);
    root.setRoleHierarchy(roleHierarchy);
    ctx.setRootObject(root);

    return ctx;

最后你可以在你的配置中使用这个自定义的DefaultMethodSecurityExpressionHandler

<global-method-security>
  <expression-handler ref="customMethodSecurityExpressionHandler"/>
</global-method-security>

并使用

@PreAuthorize("hasAnyAuthorityWithCodes('PERMISSION_UPDATE_OWN_DECISION','PERMISSION_UPDATE_ANY_DECISION')")

更新

子类 DefaultMethodSecurityExpressionHandler 并覆盖 createSecurityExpressionRoot 方法

    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
        Authentication authentication, MethodInvocation invocation) 
        MethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(
                authentication);
        root.setThis(invocation.getThis());
        root.setTrustResolver(trustResolver);
        root.setPermissionEvaluator(permissionEvaluator);
        root.setRoleHierarchy(roleHierarchy);
        root.setDefaultRolePrefix(defauleRolePrefix);

        return root;
    

【讨论】:

感谢您的回答。现在我有以下错误:无法从 AbstractSecurityExpressionHandler CustomMethodSecurityExpressionHandler.java 覆盖最终方法 createEvaluationContext 这是 Spring Security Core 4.0.3.RELEASE 现在 getTrustResolver() 和 getDefaultRolePrefix() 方法在我的 CustomMethodSecurityExpressionHandler 中都未定义 你知道如何调整它吗?谢谢 查看更新以修复。 谢谢,但也许我做错了什么 - 我无法访问 trustResolver、permissionEvaluator 和 roleHierarchy,因为它们是私有的,无法从我的 CustomMethodSecurityExpressionHandler 访问

以上是关于权限代码和名称的 Spring Security 表达式的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security权限管理

Spring Security 配置中的单角色多 IP 地址

Spring Security 权限管理

Spring Security 权限管理

Spring security 和 AOP 学习

spring security认证源码分析之账户权限