将GrantedAuthority视为“权限”或“权利”。这些“权限”通常表示为字符串(使用
getAuthority()
方法)。这些字符串可让您标识权限,并让您的投票者决定是否授予对某些内容的访问权限。
您可以通过将它们放入安全上下文中来向用户授予不同的GrantedAuthority(权限)。通常,您可以通过实现自己的UserDetailsService并返回一个返回所需GrantedAuthorities的UserDetails实现来实现这一点。
在许多示例中使用的角色(Roles)只是具有命名约定的“权限”,这个命名约定表明,角色是以前缀ROLE_
开头的GrantedAuthority。没有其他的东西。角色只是一个GrantedAuthority - 一种“权限” - 一种“权利”。在Spring Security中,你会看到很多地方特别处理带有ROLE_
前缀的角色,比如在RoleVoter中,ROLE_
前缀被用作默认值。这使你可以提供不带ROLE_
前缀的角色名称。在Spring Security 4之前,“角色”的这种特殊处理并不一致,权限和角色经常被视为相同的概念(例如,在SecurityExpressionRoot的hasAuthority()
方法的实现中,它只是调用了hasRole()
)。在Spring Security 4中,对角色的处理更加一致,处理“角色”的代码(如RoleVoter、hasRole
表达式等)总是为你添加ROLE_
前缀。因此,hasAuthority('ROLE_ADMIN')
与hasRole('ADMIN')
意思相同,因为ROLE_
前缀会自动添加。请参阅Spring Security 3到4的migration guide获取更多信息。
但仍然需要注意:角色只是具有特殊前缀“ROLE_”的权限。因此,在Spring Security 3中,
@PreAuthorize("hasRole('ROLE_XYZ')")
与
@PreAuthorize("hasAuthority('ROLE_XYZ')")
相同,在Spring Security 4中,
@PreAuthorize("hasRole('XYZ')")
与
@PreAuthorize("hasAuthority('ROLE_XYZ')")
相同。
关于您的用例:
用户拥有角色,角色可以执行某些操作。
您可以在
GrantedAuthorities
中找到用户所属角色和角色可以执行的操作。角色的
GrantedAuthorities
具有前缀
ROLE_
,而操作具有前缀
OP_
。操作权限的示例可以是
OP_DELETE_ACCOUNT
、
OP_CREATE_USER
、
OP_RUN_BATCH_JOB
等。角色可以是
ROLE_ADMIN
、
ROLE_USER
、
ROLE_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_ADMIN
,
OP_DELETE_ACCOUNT
等。当用户通过身份验证时,请确保从UserDetails.getAuthorities()方法返回其所有角色和相应操作的所有GrantedAuthorities。
例如:具有ID
ROLE_ADMIN
的管理员角色分配了
OP_DELETE_ACCOUNT
、
OP_READ_ACCOUNT
、
OP_RUN_BATCH_JOB
等操作。具有ID
ROLE_USER
的用户角色分配了操作
OP_READ_ACCOUNT
。
如果管理员登录,则生成的安全上下文将具有以下GrantedAuthorities:
ROLE_ADMIN
,
OP_DELETE_ACCOUNT
,
OP_READ_ACCOUNT
,
OP_RUN_BATCH_JOB
。
如果用户登录,则生成的安全上下文将具有以下GrantedAuthorities:
ROLE_USER
,
OP_READ_ACCOUNT
。
UserDetailsService将负责收集所有角色及其所有操作,并通过返回的UserDetails实例中的getAuthorities()方法使它们可用。
hasRole('xyz')
需要在角色名前加上 ROLE_ 前缀,而hasAuthority('xyz')
不需要前缀,并且准确地评估传入的内容。我曾经使用了这个解决方案,但在使用hasRole('OP_MY_PERMISSION')
时遇到问题,因为需要 ROLE_ 前缀。相反,应该使用hasAuthority('OP_MY_PERMISSION')
,因为我没有前缀。 - randal4<sec:authorize access="hasRole('ADMIN')">
与<sec:authorize access="hasRole('ROLE_ADMIN')">
是相同的。 - Alex78191@PreAuthorize("hasRole('ROLE_XYZ')")
实际上会检查是否有一个名为ROLE_ROLE_XYZ
的权限,因为它会为您添加前缀ROLE_
。 - James