Spring Security中角色和GrantedAuthority之间的区别


227

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

我希望对允许的操作(例如createSubUsersdeleteAccounts)进行操作,这些操作将允许管理员(具有role ROLE_ADMIN)使用。

我在网上看到的教程/演示让我感到困惑。我尝试将我阅读的内容联系起来,但我认为我们可以将两者互换。

我看到正在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)

还是有其他方法可以区分其他两个?还是不受支持,我们必须自己做?

Answers:


365

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

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

角色(在许多示例中使用过)只是“权限”,使用命名约定表示角色是以prefix开头的GrantedAuthority ROLE_。没什么了 一个角色就是一个GrantedAuthority-一个“权限”-一个“权利”。您会在Spring Security中看到很多地方,带有角色ROLE_前缀的角色是专门处理的,例如在RoleVoter中,ROLE_前缀作为默认角色。这样,您就可以提供角色名称而无需添加ROLE_前缀。在Spring安全4之前,没有非常一致地遵循对“角色”的这种特殊处理,并且权限和角色通常被视为相同(例如hasAuthority()hasRole())。使用Spring Security 4,角色的处理更加一致,处理“角色”的代码(如RoleVoterhasRole表达式等)总是ROLE_为您添加前缀。所以hasAuthority('ROLE_ADMIN')指一样hasRole('ADMIN'),因为ROLE_前缀被自动添加。有关更多信息,请参见Spring Security 3到4 迁移指南

但是,角色仍然只是带有特殊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等等。在对用户进行身份验证时,请确保从UserDetails.getAuthorities()返回了其所有角色的所有GrantedAuthority和相应的操作方法。

例如:ID为admin角色ROLE_ADMIN有操作OP_DELETE_ACCOUNTOP_READ_ACCOUNTOP_RUN_BATCH_JOB分配给它。ID为的用户角色具有ROLE_USER操作OP_READ_ACCOUNT

如果造成安全上下文管理员日志将有GrantedAuthorities: , ROLE_ADMIN,,OP_DELETE_ACCOUNTOP_READ_ACCOUNTOP_RUN_BATCH_JOB

如果一个用户登录它,它就会有: ROLE_USEROP_READ_ACCOUNT

UserDetailsS​​ervice将注意收集所有角色以及这些角色的所有操作,并通过返回的UserDetails实例中的getAuthorities()方法使它们可用。


2
谢谢!我到处都在寻找为什么“ hasRole('rolename')”在Spring 4中不起作用的原因->他们的文档浏览起来并不很快。只需快速“查找并更换”,我就回到了正轨!
约尔根·斯科尔·菲舍尔(JørgenSkårFischer)2016年

12
这是一个很好的答案。有一件事需要说明,这需要有所不同,hasRole('xyz')Spring Security 4期望您具有ROLE_前缀,而hasAuthority('xyz')期望不具有该前缀并准确评估传入的内容。我使用了这种解决方案,但是hasRole('OP_MY_PERMISSION')由于该ROLE_需要前缀。相反,我应该一直使用,hasAuthority('OP_MY_PERMISSION')因为我没有前缀。
randal4'3


1
在JSTL中,springframework.org<sec:authorize access="hasRole('ADMIN')"><sec:authorize access="hasRole('ROLE_ADMIN')">
security / tags

1
是。OP_只是一个任意前缀。ROLE_在某些spring实现中具有其特殊的含义。
詹姆斯(James)

9

AFAIK GrantedAuthority和角色在春季安全性中相同。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>

并非您要找的确切溶胶,但希望对您有所帮助

编辑:回复您的评论

角色就像是弹簧安全中的许可。在hasRole中使用intercept-url,可以很好地控制针对哪个角色/权限进行的操作。

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

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


1
谢谢您的帮助。层次结构似乎是另外一回事。我现在想做的方式(如果我必须使用Spring Security)是将角色和权限存储在同一列表中,并使用hasRole谓词对这两者进行检查。多考虑一下-Spring Security的人员可能有意这样做吗?除了在角色和特权/权限或其他授权应用程序的完整列表上使用hasRole进行检查以外,是否可以使用拦截URL。另外,需要在前缀ROLE_之前添加什么?这是惯例吗?
2013年

7

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

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

同样,在到达控制器之前,通过请求过滤器方法对ROLE进行过滤,从而在请求的处理流程中深入执行权限。最佳实践规定了在业务层中通过Controller实施权限实施。

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

在请求处理流程的深处强制执行的权限允许对权限进行更细粒度的应用。例如,用户可能具有“读写”权限以首先对资源进行分级,但是仅对子资源具有“读取”权限。拥有ROLE_READER会限制他编辑第一级资源的权限,因为他需要具有写入权限才能编辑该资源,但是@PreAuthorize拦截器可能会阻止他尝试编辑子资源。

杰克


2

就像其他人提到的那样,我认为角色是获得更细化权限的容器。

尽管我发现层次角色实现缺少对这些精细权限的精细控制。
因此,我创建了一个库来管理关系,并在安全上下文中将权限作为已授予的权限注入。

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

或更具体的权限,例如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://stackoverflow.com/a/60251931/1308685

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.