本文目录导读:

我来为你提供一个完整的RBAC(基于角色的访问控制)案例,从需求分析到代码实现。
业务场景
假设我们要为一个企业管理系统设计RBAC权限模型,包含用户管理、项目管理、任务管理三个核心模块。
数据库设计
基础表结构
-- 用户表
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
email VARCHAR(100),
status TINYINT DEFAULT 1 COMMENT '1:启用,0:禁用',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 角色表
CREATE TABLE roles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50) NOT NULL UNIQUE,
role_code VARCHAR(50) NOT NULL UNIQUE,
description VARCHAR(200),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 权限表
CREATE TABLE permissions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
perm_name VARCHAR(50) NOT NULL,
perm_code VARCHAR(50) NOT NULL UNIQUE,
module VARCHAR(50) COMMENT '所属模块',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 用户-角色关联表
CREATE TABLE user_roles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
UNIQUE KEY uk_user_role (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);
-- 角色-权限关联表
CREATE TABLE role_permissions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_id BIGINT NOT NULL,
permission_id BIGINT NOT NULL,
UNIQUE KEY uk_role_perm (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id),
FOREIGN KEY (permission_id) REFERENCES permissions(id)
);
初始化数据
-- 插入权限数据
INSERT INTO permissions (perm_name, perm_code, module) VALUES
-- 用户管理模块
('查询用户', 'user:list', 'user'),
('创建用户', 'user:create', 'user'),
('编辑用户', 'user:edit', 'user'),
('删除用户', 'user:delete', 'user'),
-- 项目管理模块
('查看项目', 'project:view', 'project'),
('创建项目', 'project:create', 'project'),
('编辑项目', 'project:edit', 'project'),
('删除项目', 'project:delete', 'project'),
-- 任务管理模块
('查看任务', 'task:view', 'task'),
('创建任务', 'task:create', 'task'),
('编辑任务', 'task:edit', 'task'),
('删除任务', 'task:delete', 'task'),
('分配任务', 'task:assign', 'task');
-- 插入角色数据
INSERT INTO roles (role_name, role_code, description) VALUES
('系统管理员', 'ADMIN', '拥有所有权限'),
('项目经理', 'PROJECT_MANAGER', '管理项目和任务'),
('开发人员', 'DEV', '处理任务'),
('访客', 'GUEST', '只读权限');
-- 分配角色权限
-- 管理员拥有所有权限
INSERT INTO role_permissions (role_id, permission_id)
SELECT 1, id FROM permissions;
-- 项目经理(查看项目、创建项目、编辑项目、删除项目、查看任务、创建任务、分配任务)
INSERT INTO role_permissions (role_id, permission_id) VALUES
(2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 10), (2, 13);
-- 开发人员(查看任务、编辑任务)
INSERT INTO role_permissions (role_id, permission_id) VALUES
(3, 9), (3, 11);
-- 访客(查看项目、查看任务)
INSERT INTO role_permissions (role_id, permission_id) VALUES
(4, 5), (4, 9);
Java代码实现
权限注解定义
package com.example.rbac.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
String value();
}
核心实体类
package com.example.rbac.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Set;
@Data
public class User {
private Long id;
private String username;
private String password;
private String email;
private Integer status;
private LocalDateTime createdAt;
private Set<Role> roles;
}
@Data
public class Role {
private Long id;
private String roleName;
private String roleCode;
private String description;
private Set<Permission> permissions;
}
@Data
public class Permission {
private Long id;
private String permName;
private String permCode;
private String module;
}
权限服务层
package com.example.rbac.service;
import com.example.rbac.entity.Permission;
import com.example.rbac.entity.Role;
import com.example.rbac.entity.User;
public interface PermissionService {
/**
* 获取用户所有权限
*/
Set<String> getUserPermissions(Long userId);
/**
* 验证用户是否拥有指定权限
*/
boolean hasPermission(Long userId, String permissionCode);
/**
* 获取用户角色列表
*/
Set<Role> getUserRoles(Long userId);
}
@Service
public class PermissionServiceImpl implements PermissionService {
@Autowired
private UserRoleMapper userRoleMapper;
@Autowired
private RolePermissionMapper rolePermissionMapper;
@Override
public Set<String> getUserPermissions(Long userId) {
// 使用缓存提升性能
String cacheKey = "user:permissions:" + userId;
Set<String> permissions = redisUtil.get(cacheKey);
if (permissions == null) {
// 查询用户角色
List<Role> roles = userRoleMapper.findRolesByUserId(userId);
// 通过角色获取权限
Set<String> permCodes = new HashSet<>();
for (Role role : roles) {
List<Permission> permissions = rolePermissionMapper.findPermissionsByRoleId(role.getId());
permissions.forEach(perm -> permCodes.add(perm.getPermCode()));
}
permissions = permCodes;
// 缓存10分钟
redisUtil.set(cacheKey, permissions, 600);
}
return permissions;
}
@Override
public boolean hasPermission(Long userId, String permissionCode) {
Set<String> permissions = getUserPermissions(userId);
return permissions.contains(permissionCode);
}
}
自定义权限拦截器
package com.example.rbac.interceptor;
import com.example.rbac.annotation.RequiresPermission;
import com.example.rbac.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class PermissionInterceptor implements HandlerInterceptor {
@Autowired
private PermissionService permissionService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 如果不是映射到方法,直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
RequiresPermission requiresPermission = handlerMethod.getMethodAnnotation(RequiresPermission.class);
if (requiresPermission == null) {
return true;
}
// 获取当前登录用户ID(这里假设从Session中获取)
Long userId = (Long) request.getSession().getAttribute("userId");
if (userId == null) {
response.setStatus(401);
return false;
}
// 验证权限
if (!permissionService.hasPermission(userId, requiresPermission.value())) {
response.setStatus(403);
response.setContentType("application/json");
response.getWriter().write("{\"code\":403,\"message\":\"权限不足\"}");
return false;
}
return true;
}
}
控制器示例
package com.example.rbac.controller;
import com.example.rbac.annotation.RequiresPermission;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/projects")
public class ProjectController {
@GetMapping
@RequiresPermission("project:view")
public Result listProjects() {
// 查看项目列表
return Result.success();
}
@PostMapping
@RequiresPermission("project:create")
public Result createProject(@RequestBody ProjectDTO projectDTO) {
// 创建项目
return Result.success();
}
@PutMapping("/{id}")
@RequiresPermission("project:edit")
public Result editProject(@PathVariable Long id, @RequestBody ProjectDTO projectDTO) {
// 编辑项目
return Result.success();
}
@DeleteMapping("/{id}")
@RequiresPermission("project:delete")
public Result deleteProject(@PathVariable Long id) {
// 删除项目
return Result.success();
}
}
动态菜单和按钮控制
package com.example.rbac.controller;
@RestController
@RequestMapping("/api/menu")
public class MenuController {
@Autowired
private PermissionService permissionService;
@GetMapping("/user")
public Result getUserMenus(Long userId) {
// 获取用户所有权限码
Set<String> permissions = permissionService.getUserPermissions(userId);
// 构建菜单树
List<MenuVO> menus = new ArrayList<>();
// 根据权限动态生成菜单
if (permissions.contains("user:list")) {
menus.add(new MenuVO("用户管理", "/user/list", "user"));
}
if (permissions.contains("project:view")) {
menus.add(new MenuVO("项目管理", "/project/list", "project"));
}
if (permissions.contains("task:view")) {
menus.add(new MenuVO("任务管理", "/task/list", "task"));
}
return Result.success(menus);
}
}
MyBatis Mapper示例
@Mapper
public interface UserRoleMapper {
@Select("SELECT r.* FROM roles r " +
"INNER JOIN user_roles ur ON r.id = ur.role_id " +
"WHERE ur.user_id = #{userId}")
List<Role> findRolesByUserId(Long userId);
@Insert("INSERT INTO user_roles (user_id, role_id) VALUES (#{userId}, #{roleId})")
int insertUserRole(@Param("userId") Long userId, @Param("roleId") Long roleId);
@Delete("DELETE FROM user_roles WHERE user_id = #{userId} AND role_id = #{roleId}")
int deleteUserRole(@Param("userId") Long userId, @Param("roleId") Long roleId);
}
@Mapper
public interface RolePermissionMapper {
@Select("SELECT p.* FROM permissions p " +
"INNER JOIN role_permissions rp ON p.id = rp.permission_id " +
"WHERE rp.role_id = #{roleId}")
List<Permission> findPermissionsByRoleId(Long roleId);
@Insert("INSERT INTO role_permissions (role_id, permission_id) VALUES (#{roleId}, #{permissionId})")
int insertRolePermission(@Param("roleId") Long roleId, @Param("permissionId") Long permissionId);
@Delete("DELETE FROM role_permissions WHERE role_id = #{roleId}")
int deleteRolePermissions(Long roleId);
}
前端按钮权限控制
// 权限指令定义
Vue.directive('permission', {
bind(el, binding) {
const requiredPermission = binding.value;
const userPermissions = store.state.userPermissions || [];
if (!userPermissions.includes(requiredPermission)) {
el.parentNode.removeChild(el);
}
}
});
// 按钮控制示例
<el-button
v-permission="'project:delete'"
type="danger"
@click="deleteProject">
删除项目
</el-button>
<el-button
v-if="checkPermission('task:assign')"
type="primary">
分配任务
</el-button>
测试用例
@SpringBootTest
public class RbacTest {
@Autowired
private PermissionService permissionService;
@Test
public void testAdminPermissions() {
// 假设用户1是管理员
assertTrue(permissionService.hasPermission(1L, "user:create"));
assertTrue(permissionService.hasPermission(1L, "project:delete"));
assertTrue(permissionService.hasPermission(1L, "task:assign"));
}
@Test
public void testDevPermissions() {
// 假设用户3是开发人员
assertTrue(permissionService.hasPermission(3L, "task:view"));
assertTrue(permissionService.hasPermission(3L, "task:edit"));
assertFalse(permissionService.hasPermission(3L, "project:delete"));
}
@Test
public void testGuestPermissions() {
// 假设用户4是访客
assertTrue(permissionService.hasPermission(4L, "project:view"));
assertTrue(permissionService.hasPermission(4L, "task:view"));
assertFalse(permissionService.hasPermission(4L, "task:edit"));
assertFalse(permissionService.hasPermission(4L, "user:create"));
}
}
性能优化建议
// 1. 使用Redis缓存权限信息
public void clearPermissionCache(Long userId) {
String cacheKey = "user:permissions:" + userId;
redisUtil.delete(cacheKey);
}
// 2. 权限变更时清除缓存
@Transactional
public void updateRolePermissions(Long roleId, List<Long> permissionIds) {
rolePermissionMapper.deleteRolePermissions(roleId);
for (Long permissionId : permissionIds) {
rolePermissionMapper.insertRolePermission(roleId, permissionId);
}
// 清除受影响用户的缓存
List<Long> userIds = userMapper.findUsersByRoleId(roleId);
userIds.forEach(this::clearPermissionCache);
}
// 3. 使用布隆过滤器快速过滤无权限请求
@Component
public class PermissionBloomFilter {
private final BloomFilter<String> filter = BloomFilter.create(
Funnels.stringFunnel(StandardCharsets.UTF_8), 100000, 0.01);
public boolean mightContain(String permCode) {
return filter.mightContain(permCode);
}
@Scheduled(initialDelay = 60000, fixedDelay = 86400000)
public void refresh() {
List<String> allPermCodes = permissionMapper.findAllPermCodes();
allPermCodes.forEach(filter::put);
}
}
这个RBAC案例涵盖了:
- 完整的数据库设计:用户、角色、权限三类核心表及关联表
- 灵活的权限控制:支持注解和动态API级别的权限校验
- 前端配合:按钮级别和菜单级别的权限控制
- 性能优化:基于Redis的缓存机制
- 完整的测试覆盖:针对不同角色的权限验证
RBAC模型的核心优势是松耦合和易于扩展,新增角色只需要配置权限组合,不需要修改业务代码。