本文目录导读:

设计一个Java角色权限案例,核心在于理解 RBAC(基于角色的访问控制) 模型,这是目前最主流、可扩展性最好的权限设计模型。
下面我将以一个 “企业内部管理系统” 为例,为你展示如何从0到1设计一个简洁但完整的角色权限案例,包含数据库设计、核心逻辑和代码示例。
核心概念:RBAC 模型
在开始编码前,先理清这几个核心概念的关系:
- 用户 (User):系统的具体操作者(张三、李四)。
- 角色 (Role):权限的集合,代表一种身份或职位(管理员、项目经理、普通员工)。
- 权限 (Permission):对某个资源(Resource)进行某种操作(Operation)的许可(“用户管理-新增”、“订单-查看”)。
- 资源 (Resource):系统操作的对象(用户、订单、报表)。
关系链(5张核心表):
用户 (User) <--多对多--> 用户角色关联表 <--多对多--> 角色 (Role) <--多对多--> 角色权限关联表 <--多对多--> 权限 (Permission)
一句话总结: 我们把权限赋予给角色,再把角色赋予给用户,用户最终拥有的权限 = 他所有角色的权限的并集。
数据库表设计(MySQL DDL 示例)
这是整个案例的基石,我们采用 5张主要表 + 2张关联表 的设计。
-- 1. 用户表 sys_user
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
nickname VARCHAR(50),
status TINYINT DEFAULT 1 COMMENT '1:启用 0:禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 2. 角色表 sys_role
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50) NOT NULL UNIQUE,
role_key VARCHAR(50) NOT NULL UNIQUE COMMENT '角色标识,如 admin, manager',
status TINYINT DEFAULT 1,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 3. 权限表 sys_permission
CREATE TABLE sys_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
perm_name VARCHAR(50) NOT NULL COMMENT '权限名称,如 用户新增',
perm_key VARCHAR(100) NOT NULL UNIQUE COMMENT '权限标识,如 sys:user:add',
parent_id BIGINT DEFAULT 0 COMMENT '父权限ID,用于构建树形菜单',
menu_name VARCHAR(50) COMMENT '菜单名称(如果是菜单级权限)',
menu_url VARCHAR(200) COMMENT '菜单URL',
perm_type ENUM('menu', 'button', 'api') DEFAULT 'api' COMMENT '权限类型: menu菜单, button按钮, api接口',
status TINYINT DEFAULT 1,
sort_order INT DEFAULT 0,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 4. 用户角色关联表 sys_user_role
-- 一个用户可以有多个角色,一个角色可以属于多个用户
CREATE TABLE sys_user_role (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id)
);
-- 5. 角色权限关联表 sys_role_permission
-- 一个角色可以有多个权限,一个权限可以属于多个角色
CREATE TABLE sys_role_permission (
role_id BIGINT NOT NULL,
permission_id BIGINT NOT NULL,
PRIMARY KEY (role_id, permission_id)
);
设计要点:
perm_key使用冒号分隔的层级格式(如sys:user:add),方便 Spring Security 等框架匹配。parent_id和perm_type用于构建树形菜单和区分按钮/API级别的控制。- 使用关联表而非直接在用户表里加角色字段,是为了支持 多角色 场景。
领域模型设计(Java 代码)
// User.java
public class User {
private Long id;
private String username;
private String password;
// 临时存放当前用户拥有的角色和权限 (从DB查询后组装)
private List<Role> roles;
private Set<String> permissions; // 存放 perm_key 集合, 如 ["sys:user:add", "sys:order:view"]
// getters and setters...
}
// Role.java
public class Role {
private Long id;
private String roleName;
private String roleKey;
private List<Permission> permissions;
// getters and setters...
}
// Permission.java
public class Permission {
private Long id;
private String permName;
private String permKey;
private Long parentId;
private String menuName;
private String menuUrl;
private String permType; // "menu", "button", "api"
// getters and setters...
}
核心业务逻辑层
登录与授权(核心方法)
通常在 UserService 或 AuthService 中实现。
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private PermissionMapper permissionMapper;
/**
* 根据用户名获取用户及其完整权限信息。
* 前端登录成功后,后端将此对象放入Session或生成JWT Token返回。
*/
public User getUserWithPermissions(String username) {
// 1. 查询用户基本信息
User user = userMapper.findByUsername(username);
if (user == null) return null;
// 2. 查询用户的所有角色
List<Role> roles = roleMapper.findRolesByUserId(user.getId());
user.setRoles(roles);
// 3. 查询用户所有角色的所有权限(去重)
Set<String> permissions = new HashSet<>();
for (Role role : roles) {
List<Permission> perms = permissionMapper.findPermissionsByRoleId(role.getId());
for (Permission perm : perms) {
// 只收集api和button类型的权限标识进行控制
if (!"menu".equals(perm.getPermType())) {
permissions.add(perm.getPermKey());
}
}
}
user.setPermissions(permissions);
return user;
}
}
权限检查工具(核心判断逻辑)
/**
* 在当前线程请求中检查用户是否有权限。
* 一般配合 Spring AOP 或 Filter 使用。
*/
@Service
public class PermissionCheckService {
// 模拟从 ThreadLocal 或 SecurityContextHolder 获取当前用户
public boolean hasPermission(String requiredPermKey) {
// 1. 获取当前登录用户
User currentUser = getCurrentUser();
if (currentUser == null) return false;
// 2. 检查用户的权限集合是否包含所需权限
boolean has = currentUser.getPermissions().contains(requiredPermKey);
System.out.println("检查权限 [" + requiredPermKey + "] 结果: " + has);
return has;
}
}
权限控制实现(结合 Spring)
方案A:基于注解(推荐,最优雅)
// 1. 自定义权限注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String value(); // 权限标识, 如 "sys:user:add"
}
// 2. 使用 Spring AOP 实现拦截逻辑
@Aspect
@Component
public class PermissionAspect {
@Autowired
private PermissionCheckService permissionCheckService;
// 拦截所有带有 @RequirePermission 注解的方法
@Around("@annotation(requirePermission)")
public Object checkPermission(ProceedingJoinPoint joinPoint, RequirePermission requirePermission) throws Throwable {
String requiredPerm = requirePermission.value();
// 调用核心检查逻辑
if (permissionCheckService.hasPermission(requiredPerm)) {
return joinPoint.proceed(); // 有权限,继续执行
} else {
// 无权限,抛出异常或返回错误信息
throw new RuntimeException("No Permission: " + requiredPerm);
}
}
}
// 3. 在Controller中使用
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
@RequirePermission("sys:user:add")
public ResponseEntity<String> addUser(@RequestBody User user) {
// 只有拥有 sys:user:add 权限的用户才能访问
return ResponseEntity.ok("用户创建成功");
}
@GetMapping
@RequirePermission("sys:user:list")
public ResponseEntity<List<User>> listUsers() {
// 只有拥有 sys:user:list 权限的用户才能访问
return ResponseEntity.ok(userService.findAll());
}
}
方案B:基于 Filter/Interceptor
在 Spring Security 或自定义 Filter 中,对请求的URL与权限进行匹配。
// 伪代码
@WebFilter(urlPatterns = "/api/*")
public class PermissionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 1. 解析请求URL,判断是否受保护的资源(/api/users/add)
String requestURI = extractUrl(request);
// 2. 根据请求方法+URL 查询数据库,找到需要的权限标识(sys:user:add)
String requiredPermKey = getRequiredPermFromUrl(requestURI, request.getMethod());
if (requiredPermKey == null) {
// 该URL不需要权限,直接放行
chain.doFilter(request, response);
} else {
// 3. 调用核心检查逻辑
if (permissionCheckService.hasPermission(requiredPermKey)) {
chain.doFilter(request, response);
} else {
// 4. 无权限,返回403
((HttpServletResponse) response).sendError(403, "无权限访问");
}
}
}
}
前端配合
- 登录后获取权限:用户登录成功后,后端返回
user.permissions(权限标识集合)。 - 菜单动态生成:前端根据
user.roles或user.permissions过滤菜单列表(只显示有权限的菜单)。 - 按钮级显隐:在模板中使用类似
v-if="hasPermission('sys:user:add')"的指令来控制新增按钮的显示与隐藏。
一个完整的实战案例流程
假设有一个 “订单管理系统”:
- 管理员:拥有
order:create,order:view,order:edit,order:delete所有权限。 - 客服人员:只拥有
order:view,order:edit权限。 - 实习生:只拥有
order:view权限。
操作验证:
- 实习生 登录后,前端订单列表的“新增”、“编辑”、“删除”按钮都不可见;通过API直接调用创建接口会返回403。
- 客服人员 登录后,可以看到“编辑”按钮,但看不到“新增”和“删除”按钮。
- 管理员 可以看到所有按钮,并能操作所有功能。
总结与扩展
这是一个经典的、开箱即用的RBAC案例。 它给出了清晰的数据库结构、Java实体、权限检查逻辑和Spring集成方式。
进阶扩展点(当你需要更复杂的场景时):
- 数据权限:除了功能权限,可能还需要控制数据行,客服A只能看自己的客户订单,经理能看所有,这需要在SQL中根据用户角色ID或部门ID添加额外的
WHERE条件。 - 缓存:用户权限通常是低频变动的,可以将用户的角色和权限信息缓存到Redis中,提升性能。
- Spring Security集成:上述AOP方案的理念与Spring Security的
@PreAuthorize注解高度一致,你可以直接使用Spring Security提供的hasPermission表达式,它更成熟和完善。
这个设计案例可以很好地满足中小型系统的权限管理需求。