Spring Security 中 Role 和 GrantedAuthority 的区别

Posted

技术标签:

【中文标题】Spring Security 中 Role 和 GrantedAuthority 的区别【英文标题】:Difference between Role and GrantedAuthority in Spring Security 【发布时间】:2013-10-31 18:24:06 【问题描述】:

Spring Security 中有一些概念和实现,例如GrantedAuthority 接口可以获取权限 来授权/控制访问。

我希望允许的操作,例如 createSubUsersdeleteAccounts,我将允许 admin(角色 @ 987654322@)。

我对网上看到的教程/演示感到困惑。我试图把我读到的东西联系起来,但我认为我们可以互换地对待这两者。

我看到hasRole 使用GrantedAuthority 字符串?我绝对在理解上做错了。 Spring Security 在概念上是什么?

我如何存储用户的角色,与该角色的权限分开?

我还在查看org.springframework.security.core.userdetails.UserDetails 接口,该接口在身份验证提供程序引用的 DAO 中使用,它使用 User(注意最后的 GrantedAuthority):

public User(String username, 
            String password, 
            boolean enabled, 
            boolean accountNonExpired,
            boolean credentialsNonExpired, 
            boolean accountNonLocked, 
            Collection<? extends GrantedAuthority> authorities)

或者还有其他方法可以区分其他两者吗?还是不支持,我们必须自己制作?

【问题讨论】:

【参考方案1】:

AFAIK GrantedAuthority 和角色在 Spring Security 中是相同的。 GrantedAuthority 的 getAuthority() 字符串是角色(根据默认实现 SimpleGrantedAuthority)。

对于您的情况,您可以使用分层角色

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_createSubUsers
            ROLE_ADMIN > ROLE_deleteAccounts 
            ROLE_USER > ROLE_viewAccounts
        </value>
    </property>
</bean>

不是您正在寻找的确切溶胶,但希望对您有所帮助

编辑:回复您的评论

角色就像 spring-security 中的权限。将拦截 url 与 hasRole 结合使用可以非常精细地控制允许哪些操作用于哪些角色/权限。

我们在应用程序中处理的方式是,我们为每个操作(或休息 url)定义权限(即角色),例如view_account、delete_account、add_account 等。然后我们为每个用户创建逻辑配置文件,如 admin、guest_user、normal_user。配置文件只是权限的逻辑分组,独立于 spring-security。 添加新用户时,会为其分配一个配置文件(具有所有允许的权限)。现在,当用户尝试执行某项操作时,该操作的权限/角色会根据用户grantedAuthorities 进行检查。

另外,默认的 RoleVoter 使用前缀 ROLE_,因此任何以 ROLE_ 开头的权限都被视为角色,您可以通过在角色投票者中使用自定义 RolePrefix 并在 Spring Security 中使用来更改此默认行为。

【讨论】:

感谢您的帮助。层次结构看起来是别的东西。我现在正在考虑这样做的方式(如果我必须使用 Spring Security)将角色和权限存储在同一个列表中,并使用 hasRole 谓词对两者进行检查。多想一想——这可能是 Spring Security 的人故意留下的?除了在角色和权限/权限的完整列表上使用 hasRole 或其他授权应用程序检查之外,是否有可能使用拦截 URL。另外,需要前缀 ROLE_ 吗?是约定俗成的吗?【参考方案2】:

将 GrantedAuthority 视为“许可”或“权利”。这些“权限”(通常)表示为字符串(使用getAuthority() 方法)。这些字符串可让您识别权限并让您的选民决定他们是否授予对某些内容的访问权限。

您可以通过将用户放入安全上下文中来向用户授予不同的 GrantedAuthority(权限)。您通常通过实现自己的 UserDetailsS​​ervice 来实现,该服务返回返回所需的 GrantedAuthorities 的 UserDetails 实现。

角色(如在许多示例中使用的那样)只是“权限”,其命名约定表明角色是以前缀 ROLE_ 开头的 GrantedAuthority。没有什么了。角色只是一个 GrantedAuthority - 一个“权限” - 一个“权利”。您会在 Spring Security 中看到很多地方,其中带有 ROLE_ 前缀的角色被特殊处理,例如在 RoleVoter 中,默认使用 ROLE_ 前缀。这允许您提供不带ROLE_ 前缀的角色名称。在 Spring security 4 之前,这种对“角色”的特殊处理并没有得到非常一致的遵循,并且权限和角色通常被视为相同(例如,您可以在 SecurityExpressionRoot 中的 hasAuthority() 方法的实现中看到 - 这只是致电hasRole())。在 Spring Security 4 中,角色的处理更加一致,处理“角色”的代码(如 RoleVoterhasRole 表达式等)总是为您添加 ROLE_ 前缀。所以hasAuthority('ROLE_ADMIN') 的含义与hasRole('ADMIN') 相同,因为ROLE_ 前缀是自动添加的。有关更多信息,请参阅 spring security 3 到 4 migration guide。

但仍然:角色只是具有特殊ROLE_ 前缀的权限。所以在 Spring 安全 3 中 @PreAuthorize("hasRole('ROLE_XYZ')")@PreAuthorize("hasAuthority('ROLE_XYZ')") 相同,在 Spring 安全 4 中 @PreAuthorize("hasRole('XYZ')")@PreAuthorize("hasAuthority('ROLE_XYZ')") 相同。

关于您的用例:

用户具有角色,角色可以执行某些操作。

对于用户所属的角色以及角色可以执行的操作,您最终可能会出现在 GrantedAuthorities 中。角色的GrantedAuthorities 具有前缀ROLE_,操作具有前缀OP_。操作权限的示例可以是OP_DELETE_ACCOUNTOP_CREATE_USEROP_RUN_BATCH_JOB等。角色可以是ROLE_ADMINROLE_USERROLE_OWNER 等。

您最终可以让您的实体实现GrantedAuthority,就像在这个(伪代码)示例中一样:

@Entity
class Role implements GrantedAuthority 
    @Id
    private String id;

    @ManyToMany
    private final List<Operation> allowedOperations = new ArrayList<>();

    @Override
    public String getAuthority() 
        return id;
    

    public Collection<GrantedAuthority> getAllowedOperations() 
        return allowedOperations;
    


@Entity
class User 
    @Id
    private String id;

    @ManyToMany
    private final List<Role> roles = new ArrayList<>();

    public Collection<Role> getRoles() 
        return roles;
    


@Entity
class Operation implements GrantedAuthority 
    @Id
    private String id;

    @Override
    public String getAuthority() 
        return id;
    

您在数据库中创建的角色和操作的 ID 将是 GrantedAuthority 表示,例如ROLE_ADMINOP_DELETE_ACCOUNT等。当一个用户被认证时,确保其所有角色的所有GrantedAuthorities和相应的操作都是从UserDetails.getAuthorities()方法返回的。

示例: id 为ROLE_ADMIN 的管理员角色分配了OP_DELETE_ACCOUNTOP_READ_ACCOUNTOP_RUN_BATCH_JOB 操作。 id为ROLE_USER的用户角色有操作OP_READ_ACCOUNT

如果管理员登录生成的安全上下文将具有 GrantedAuthorities: ROLE_ADMINOP_DELETE_ACCOUNTOP_READ_ACCOUNTOP_RUN_BATCH_JOB

如果用户登录它,它将具有: ROLE_USEROP_READ_ACCOUNT

UserDetailsS​​ervice 会小心收集所有角色和这些角色的所有操作,并通过返回的 UserDetails 实例中的 getAuthorities() 方法使它们可用。

【讨论】:

谢谢!我一直在到处寻找为什么“hasRole('rolename')”在 Spring 4 中不起作用 -> 他们的文档并不能很快浏览。只需快速“查找和替换”,我就回到了正轨! 这是一个很好的答案。有一点需要明确说明,Spring Security 4 中的 hasRole('xyz') 期望您具有 ROLE_ 前缀,而 hasAuthority('xyz') 不期望前缀并准确评估传入的内容. 我使用了这个解决方案,但遇到了hasRole('OP_MY_PERMISSION') 的问题,因为需要 ROLE_ 前缀。相反,我应该使用hasAuthority('OP_MY_PERMISSION'),因为我没有前缀。 在 JSTL springframework.org/security/tags &lt;sec:authorize access="hasRole('ADMIN')"&gt;&lt;sec:authorize access="hasRole('ROLE_ADMIN')"&gt; 相同 是的。 OP_ 只是一个任意前缀。 ROLE_在一些spring的实现中有着特殊的意义。 @gautam:不不,这是正确的。 @PreAuthorize("hasRole('ROLE_XYZ')") 实际上会检查是否存在名为 ROLE_ROLE_XYZ 的权限,因为它会为您添加 ROLE_ 前缀。【参考方案3】:

理解这些概念之间关系的另一种方法是将 ROLE 解释为权限的容器。

权限是针对特定操作的细粒度权限,有时与特定数据范围或上下文相结合。例如,读取、写入、管理可以表示对给定信息范围的不同级别的权限。

此外,权限在请求的处理流程中被强制执行,而 ROLE 在到达控制器之前通过请求过滤器方式过滤。最佳实践规定在业务层中通过控制器实施权限强制执行。

另一方面,ROLES 是一组权限的粗粒度表示。 ROLE_READER 将仅具有读取或查看权限,而 ROLE_EDITOR 将同时具有读取和写入权限。角色主要用于外围请求处理的第一次筛选如 http。 ... .antMatcher(...).hasRole(ROLE_MANAGER)

在请求的流程中深入实施的权限允许更细粒度地应用权限。例如,用户可能具有对一级资源的读写权限,但仅对子资源具有读取权限。拥有 ROLE_READER 会限制他编辑第一级资源的权利,因为他需要写入权限才能编辑此资源,但 @PreAuthorize 拦截器可能会阻止他尝试编辑子资源。

杰克

【讨论】:

【参考方案4】:

就像其他人提到的那样,我将角色视为更精细权限的容器。

虽然我发现 Hierarchy Role 实现缺乏对这些细粒度权限的精细控制。 所以我创建了一个库来管理关系并将权限注入安全上下文中的授予权限。

我可能在应用程序中有一组权限,例如 CREATE、READ、UPDATE、DELETE,然后与用户的角色相关联。

或更具体的权限,如 READ_POST、READ_PUBLISHED_POST、CREATE_POST、PUBLISH_POST

这些权限是相对静态的,但角色与它们的关系可能是动态的。

示例 -

@Autowired 
RolePermissionsRepository repository;

public void setup()
  String roleName = "ROLE_ADMIN";
  List<String> permissions = new ArrayList<String>();
  permissions.add("CREATE");
  permissions.add("READ");
  permissions.add("UPDATE");
  permissions.add("DELETE");
  repository.save(new RolePermissions(roleName, permissions));

您可以创建 API 来管理这些权限与角色的关系。

我不想复制/粘贴另一个答案,所以这里是关于 SO 的更完整解释的链接。https://***.com/a/60251931/1308685

为了重用我的实现,我创建了一个 repo。请随时贡献!https://github.com/savantly-net/spring-role-permissions

【讨论】:

以上是关于Spring Security 中 Role 和 GrantedAuthority 的区别的主要内容,如果未能解决你的问题,请参考以下文章

带有 ROLE_ANONYMOUS 的 AngularJS 和 Spring Security 仍然返回 401

不再需要 Spring Security ROLE_ 前缀?

Spring Security当使用注解标记权限时修改ROLE_前缀

Spring Security 角色继承

Spring Security Role Hierarchy 无法使用 Java Config

Spring Security 实体字段级安全性