本文目录导读:

在Java中实现角色管理通常涉及用户-角色-权限的经典RBAC(基于角色的访问控制)模型,以下是一个从数据库设计到代码实现的完整案例,包含基础CRUD和安全校验。
核心概念
- 用户(User) :系统使用者
- 角色(Role) :权限的集合(如:管理员、编辑、访客)
- 权限(Permission) :具体操作(如:用户管理-新增、文章管理-删除)
关系:用户 <—多对多—> 角色 <—多对多—> 权限
数据库表设计
-- 用户表
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
status TINYINT DEFAULT 1 -- 1启用 0禁用
);
-- 角色表
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50) UNIQUE NOT NULL,
role_key VARCHAR(50) UNIQUE NOT NULL -- 角色标识(如admin)
);
-- 权限表
CREATE TABLE sys_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
perm_name VARCHAR(50) NOT NULL,
perm_key VARCHAR(100) UNIQUE NOT NULL -- 权限标识(如user:add)
);
-- 用户-角色关联表
CREATE TABLE sys_user_role (
user_id BIGINT,
role_id BIGINT,
PRIMARY KEY (user_id, role_id)
);
-- 角色-权限关联表
CREATE TABLE sys_role_perm (
role_id BIGINT,
perm_id BIGINT,
PRIMARY KEY (role_id, perm_id)
);
实体类设计(Java)
// 用户实体
public class User {
private Long id;
private String username;
private String password;
private Integer status;
private List<Role> roles; // 用户拥有的角色列表
}
// 角色实体
public class Role {
private Long id;
private String roleName;
private String roleKey;
private List<Permission> permissions; // 角色拥有的权限列表
}
// 权限实体
public class Permission {
private Long id;
private String permName;
private String permKey;
}
核心业务逻辑实现
角色CRUD(以Service层为例)
@Service
public class RoleService {
@Autowired
private RoleMapper roleMapper;
@Autowired
private RolePermissionMapper rolePermissionMapper;
// 创建角色
@Transactional
public void createRole(Role role, List<Long> permissionIds) {
// 1. 新增角色
roleMapper.insert(role);
// 2. 绑定权限
if (permissionIds != null && !permissionIds.isEmpty()) {
rolePermissionMapper.batchInsert(role.getId(), permissionIds);
}
}
// 更新角色权限
@Transactional
public void updateRolePermissions(Long roleId, List<Long> newPermissionIds) {
// 先删除原有权限关联
rolePermissionMapper.deleteByRoleId(roleId);
// 再插入新的权限关联
rolePermissionMapper.batchInsert(roleId, newPermissionIds);
}
// 删除角色(连带删除关联表数据)
@Transactional
public void deleteRole(Long roleId) {
rolePermissionMapper.deleteByRoleId(roleId); // 删除角色-权限关联
userRoleMapper.deleteByRoleId(roleId); // 删除用户-角色关联
roleMapper.deleteById(roleId); // 删除角色本身
}
}
用户-角色分配
@Service
public class UserRoleService {
// 为用户分配角色
@Transactional
public void assignRolesToUser(Long userId, List<Long> roleIds) {
// 先清除用户原有角色
userRoleMapper.deleteByUserId(userId);
// 赋予新角色
userRoleMapper.batchInsert(userId, roleIds);
}
}
权限校验(Spring Security整合)
自定义权限注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
String value(); // 如 "user:add"
}
权限切面实现
@Aspect
@Component
public class PermissionAspect {
@Around("@annotation(requiresPermission)")
public Object checkPermission(ProceedingJoinPoint joinPoint,
RequiresPermission requiresPermission) throws Throwable {
// 获取当前登录用户
User currentUser = SecurityContextHolder.getContext().getAuthentication();
// 获取用户所有权限
Set<String> userPermissions = currentUser.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
// 检查是否包含所需权限
if (!userPermissions.contains(requiresPermission.value())) {
throw new AccessDeniedException("无权限执行此操作");
}
return joinPoint.proceed();
}
}
使用示例(Controller层)
@RestController
@RequestMapping("/users")
public class UserController {
@RequiresPermission("user:add")
@PostMapping
public Result addUser(@RequestBody User user) {
userService.addUser(user);
return Result.success();
}
@RequiresPermission("user:delete")
@DeleteMapping("/{id}")
public Result deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return Result.success();
}
}
前端实现示例(Vue3 + Element Plus)
<template>
<div>
<!-- 角色列表 -->
<el-table :data="roles">
<el-table-column prop="roleName" label="角色名称" />
<el-table-column prop="roleKey" label="角色标识" />
<el-table-column label="操作">
<template #default="{ row }">
<el-button @click="editRole(row)">编辑权限</el-button>
<el-button @click="deleteRole(row.id)" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分配权限弹窗 -->
<el-dialog v-model="dialogVisible" title="分配权限">
<el-tree
ref="treeRef"
:data="allPermissions"
show-checkbox
node-key="id"
:default-checked-keys="checkedPermissions"
/>
<template #footer>
<el-button @click="savePermissions">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const roles = ref([])
const dialogVisible = ref(false)
const allPermissions = ref([])
const checkedPermissions = ref([])
const currentRoleId = ref(null)
// 获取角色列表
const fetchRoles = async () => {
const res = await axios.get('/api/roles')
roles.value = res.data
}
// 编辑角色权限
const editRole = (role) => {
currentRoleId.value = role.id
// 获取该角色已拥有的权限ID
axios.get(`/api/roles/${role.id}/permissions`).then(res => {
checkedPermissions.value = res.data // [1, 3, 5]
})
dialogVisible.value = true
}
// 保存权限分配
const savePermissions = () => {
const selectedIds = treeRef.value.getCheckedKeys()
axios.put(`/api/roles/${currentRoleId.value}/permissions`, selectedIds)
.then(() => {
ElMessage.success('权限更新成功')
dialogVisible.value = false
})
}
onMounted(fetchRoles)
</script>
面试追问与扩展
Q1:如果角色和权限数据量很大(百万级),如何优化?
- 使用Redis缓存用户权限(如:
user:permissions:{userId}),减少数据库查询。 - 权限判断走位图(BitMap) ,适用于固定权限集。
- 采用数据脱敏+读写分离。
Q2:如何实现角色互斥(一个用户不能同时拥有“管理员”和“普通用户”)?
在前端限制选择,后端插入前校验:
// 检查用户是否已有互斥角色
List<Role> existingRoles = userRoleMapper.findRolesByUserId(userId);
if (existingRoles.stream().anyMatch(r -> r.getRoleKey().equals("admin"))
&& newRoles.contains("normal")) {
throw new BusinessException("用户已是管理员,不能分配普通用户角色");
}
Q3:RBAC的扩展模型?
- RBAC1:支持角色继承(如:超级管理员 -> 管理员 -> 编辑)
- RBAC2:增加约束(如:角色互斥、基数限制)
- ABAC(基于属性的访问控制) :结合用户属性、资源属性、环境条件动态判断。
| 层级 | 实现方式 |
|---|---|
| 数据层 | 5张标准RBAC表 |
| 业务层 | 角色CRUD、用户-角色分配、权限-角色绑定 |
| 安全校验 | 自定义注解 + AOP 或 Spring Security |
| 前端 | 权限树组件、隐藏无权限按钮 |
这个案例涵盖了角色管理最核心的增删改查、权限分配以及访问控制,可以直接作为企业级后台系统的基线方案。