# 1 RBAC 权限模型
RBAC(Role-Based Access Control,基于角色的访问控制)是一种广泛使用的权限管理模型,它通过角色作为中介来连接用户和权限。

在企业管理系统设计中,采用RBAC(基于角色的访问控制)模型能大幅简化权限管理。我们不再需要为每个用户单独设置权限,而是先抽象出"销售经理"、"财务经理"、"市场经理"等角色,将权限批量分配给这些角色。
当员工入职或调岗时,只需调整其对应的角色即可自动获得相应权限。例如,同时负责销售和市场部门的王先生,只需被赋予"销售经理"和"市场经理"两个角色,就能自动继承这两个角色的所有权限。
这种方式既避免了逐个设置权限的繁琐,又便于后续权限的批量调整,使权限管理更加灵活高效。
下图展示了系统中用户、角色、菜单和权限之间的关系。

# 2 权限注解
项目使用 Spring Security 作为安全验证框架。 Spring Security 是 Spring 生态中的安全框架,用于:
- 认证(Authentication):验证用户身份(如用户名/密码登录)
- 授权(Authorization):控制用户能访问哪些资源(如
/admin仅管理员可访问)
@PreAuthorize 是 Spring Security 提供的前置权限控制注解,用于在方法执行前检查权限,通常标注在 Controller 或 Service 层的方法上。
1、基于【权限标识】的权限控制
权限标识,对应 system_menu 表的 permission 字段,推荐格式为 ${系统}:${模块}:${操作},例如说 system:admin:add 标识 system 服务的添加管理员。
使用示例如下:
// 符合 system:user:list 权限要求
@PreAuthorize("@ss.hasPermission('system:user:list')")
// 符合 system:user:add 或 system:user:edit 权限要求即可
@PreAuthorize("@ss.hasAnyPermissions('system:user:add,system:user:edit')")
2
3
4
5
2、基于【角色标识】的权限控制
权限标识,对应 system_role 表的 code 字段, 例如说 super_admin 超级管理员、tenant_admin 租户管理员。
使用示例如下:
// 属于 user 角色
@PreAuthorize("@ss.hasRole('user')")
// 属于 user 或者 admin 之一
@PreAuthorize("@ss.hasAnyRoles('user', 'admin')")
2
3
4
5
当 @PreAuthorize 注解里的 Spring EL 表达式返回 false 时,表示没有权限 。若没有权限,则会触发 AccessDeniedHandler 的 handle 方法 。
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
throws IOException, ServletException {
// 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
SecurityFrameworkUtils.getLoginUserId(), e);
// 返回 403
ServletUtils.writeJSON(response, CommonResult.error(FORBIDDEN));
}
}
2
3
4
5
6
7
8
9
10
11
# 3 实现原理
@PreAuthorize("@ss.hasPermission('system:user:list')") 表示调用 Bean 名字为 ss 的 #hasPermission(...) 方法,方法参数为 "system:user:list" 字符串。
// 使用 Spring Security 的缩写,方便使用
@Bean("ss")
public SecurityFrameworkService securityFrameworkService(PermissionAdapter permissionAdapter) {
return new SecurityFrameworkServiceImpl(permissionAdapter);
}
2
3
4
5
而 SecurityFrameworkService 提供了多个权限校验的接口:
/**
* Security 框架 Service 接口,定义权限相关的校验操作
*/
public interface SecurityFrameworkService {
/**
* 判断是否有权限
*
* @param permission 权限
* @return 是否
*/
boolean hasPermission(String permission);
/**
* 判断是否有权限,任一一个即可
*
* @param permissions 权限
* @return 是否
*/
boolean hasAnyPermissions(String... permissions);
/**
* 判断是否有角色
*
* 注意,角色使用的是 SysRoleDO 的 code 标识
*
* @param role 角色
* @return 是否
*/
boolean hasRole(String role);
/**
* 判断是否有角色,任一一个即可
*
* @param roles 角色数组
* @return 是否
*/
boolean hasAnyRoles(String... roles);
/**
* 判断是否有授权
*
* @param scope 授权
* @return 是否
*/
boolean hasScope(String scope);
/**
* 判断是否有授权范围,任一一个即可
*
* @param scope 授权范围数组
* @return 是否
*/
boolean hasAnyScopes(String... scope);
}
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
47
48
49
50
51
52
53
54
55
我们重点关注 hasPermission 实现方法。

1、获取当前用户 ID
在每次请求时候,每次都会从请求头中获取 token 数据,然后根据 token 从 Redis 中获取登录用户信息:
{
"accessToken":"d5516561a3124475a2b622eb0c8031e6",
"refreshToken":"707ba1b4fbed4280a1ce04ca429fa379",
"userId":1,
"userType":2,
"clientId":1,
"expiresTime":1753954785960
}
2
3
4
5
6
7
8
2、通过用户 ID 、权限值判断用户是否具备该权限
public boolean hasAnyPermissions(Long userId, String... permissions) {
// 如果为空,说明已经有权限
if (ArrayUtil.isEmpty(permissions)) {
return true;
}
// 获得当前登录的角色。如果为空,说明没有权限
List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
if (CollUtil.isEmpty(roles)) {
return false;
}
// 情况一:遍历判断每个权限,如果有一满足,说明有权限
for (String permission : permissions) {
if (hasAnyPermission(roles, permission)) {
return true;
}
}
// 情况二:如果是超管,也说明有权限
return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 查询用户所有的角色 ;
- 遍历权限数组,判断角色列表是否包含权限条目 ;
- 超级管理员拥有所有权限,避免权限配置遗漏。
RBAC 权限模型 参考文章:https://www.woshipm.com/pd/440765.html
