Spring Security中Role和GrantedAuthority的区别

Spring Security中Role和GrantedAuthority的区别

技术背景

在Spring Security中,权限管理是其核心功能之一。GrantedAuthority 接口和 Role(角色)是权限管理中的重要概念。GrantedAuthority 用于表示一种权限或权利,可用于授权和控制访问;而 Role 通常作为一组权限的集合,方便进行权限管理。然而,在实际应用中,很多开发者容易混淆这两个概念,不清楚它们在Spring Security中的具体含义和使用方式。

实现步骤

理解GrantedAuthority和Role的概念

  • GrantedAuthority:它代表一个“权限”或“权利”,通常通过 getAuthority() 方法返回一个字符串来标识该权限。可以通过实现自定义的 UserDetailsService,在返回的 UserDetails 实现中包含所需的 GrantedAuthority 集合,将不同的权限授予用户。
  • Role:在Spring Security的很多示例中,角色是具有特定命名约定的 GrantedAuthority,即以 ROLE_ 为前缀。例如,ROLE_ADMIN 就是一个角色。在Spring Security 4之前,权限和角色的处理不够一致;而在Spring Security 4中,角色的处理更加规范,如 hasRole 表达式会自动添加 ROLE_ 前缀。

存储用户角色和权限

可以创建实体类来实现 GrantedAuthority 接口,存储用户的角色和权限信息。以下是示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collection;

@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;
}
}

在上述代码中,Role 类和 Operation 类都实现了 GrantedAuthority 接口,分别表示角色和具体操作权限。User 类关联了多个 Role

配置权限和角色

在用户认证时,需要确保从 UserDetails.getAuthorities() 方法返回用户所有角色和相应操作的 GrantedAuthority。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.Collection;

public class CustomUserDetailsService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 假设从数据库中获取用户信息
User user = getUserFromDatabase(username);
Collection<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : user.getRoles()) {
authorities.add(role);
authorities.addAll(role.getAllowedOperations());
}
return new User(user.getUsername(), user.getPassword(), authorities);
}

private User getUserFromDatabase(String username) {
// 实现从数据库获取用户信息的逻辑
return null;
}
}

核心代码

实体类实现 GrantedAuthority 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@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 Operation implements GrantedAuthority {
@Id
private String id;

@Override
public String getAuthority() {
return id;
}
}

自定义 UserDetailsService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.Collection;

public class CustomUserDetailsService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 假设从数据库中获取用户信息
User user = getUserFromDatabase(username);
Collection<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : user.getRoles()) {
authorities.add(role);
authorities.addAll(role.getAllowedOperations());
}
return new User(user.getUsername(), user.getPassword(), authorities);
}

private User getUserFromDatabase(String username) {
// 实现从数据库获取用户信息的逻辑
return null;
}
}

最佳实践

  • 使用命名约定:为角色和权限设置清晰的命名约定,如角色使用 ROLE_ 前缀,操作权限使用 OP_ 前缀,方便区分和管理。
  • 细粒度权限控制:在业务层实现权限验证,使用 @PreAuthorize 等注解进行细粒度的权限控制,而不是仅依赖角色进行请求过滤。
  • 动态管理权限:可以创建API来管理角色和权限的关系,使权限配置更加灵活。

常见问题

hasRolehasAuthority 的区别

在Spring Security 4中,hasRole 表达式会自动添加 ROLE_ 前缀,而 hasAuthority 则不会。例如,hasRole('ADMIN') 等同于 hasAuthority('ROLE_ADMIN')

角色层次结构的使用

可以使用 Hierarchical Roles 来定义角色之间的层次关系,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
<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>

权限验证不生效

可能是由于权限配置错误、UserDetailsService 实现不正确或 GrantedAuthority 未正确返回等原因导致。需要检查权限配置和用户认证逻辑,确保所有权限都正确添加到 UserDetails 中。


Spring Security中Role和GrantedAuthority的区别
https://119291.xyz/posts/2025-04-28.difference-between-role-and-grantedauthority-in-spring-security/
作者
ww
发布于
2025年4月28日
许可协议