基于Spring Boot+MyBatis+Vue的权限管理系统实战项目
简介:权限管理系统是保障信息安全与操作规范的核心机制,广泛应用于企业级IT系统中。本项目基于Spring Boot、MyBatis和Vue.js三大主流框架构建,涵盖用户管理、角色管理、资源分配、权限控制、审计日志及动态权限调整等核心功能。通过前后端分离架构,实现安全高效的权限控制体系。项目包含完整的设计文档、数据库脚本、源码与测试用例,适合开发者学习框架集成与企业级权限管理的实战应用。
权限管理系统:从零构建高安全、可扩展的企业级RBAC实战
你有没有遇到过这样的场景?半夜接到告警,发现某个普通员工居然偷偷访问了财务系统的敏感接口;或者新来的运维同事误删了生产数据库……这些问题背后,往往都指向同一个薄弱环节—— 权限管理不到位 。
在现代企业应用中,权限系统早已不是“有就行”的功能模块,而是关乎数据安全、合规审计和业务稳定运行的 核心基础设施 。一个设计良好的权限体系,不仅能防止越权操作,还能提升团队协作效率,让组织架构与系统权限天然对齐。
今天,我们就来手把手搭建一套完整的企业级权限管理系统,涵盖前后端全链路实现。整个过程不玩虚的,直接上硬核代码+真实工程实践,带你打通权限控制的最后一公里 🚀
想象一下这个画面:你在一家金融科技公司负责后台系统开发。某天产品经理找到你说:“我们需要给交易员、风控员、管理员分配不同的操作权限,比如交易员只能提交订单,审批通过必须由风控员复核。”
这听起来是不是很熟悉?没错,这就是典型的 RBAC(基于角色的访问控制)模型 的应用场景。而我们要做的,就是把这种复杂的权限逻辑,用技术手段精准落地。
但别急着写代码!先搞清楚一件事:很多人混淆了“认证”和“授权”,结果导致系统出现严重的安全漏洞。
👉 认证(Authentication) 是确认你是谁 —— 比如登录时输入用户名密码;
👉 授权(Authorization) 是决定你能做什么 —— 比如登录后是否允许删除用户。
简单说:
🔑 认证 = “你是张三吗?”
🔐 授权 = “张三能删数据库吗?”
搞反了顺序,轻则功能错乱,重则被黑客钻空子 😱
那市面上常见的权限模型有哪些呢?我们来看个演化图谱:
graph LR
A[DAC - 自主访问控制] --> B[MAC - 强制访问控制]
B --> C[ABAC - 属性基访问控制]
C --> D[RBAC - 基于角色的访问控制]
- DAC :文件所有者自己决定谁能读写,太随意,不适合企业环境;
- MAC :操作系统级别强制策略,像军事系统那种“绝密/机密/公开”分级,太死板;
- ABAC :根据用户属性(部门、职级)、资源属性、环境条件动态判断,灵活但复杂;
- RBAC :折中方案,通过“角色”作为桥梁,既灵活又易于管理。
对于我们大多数业务系统来说, RBAC 是最佳选择 。它把“用户 ↔ 权限”的多对多关系,变成“用户 → 角色 → 权限”的链式结构,大大降低了维护成本。
举个例子:
金融系统里,“交易员”角色拥有【提交订单】权限,“风控员”角色拥有【复核订单】权限。当新人入职时,只需给他分配对应角色,无需手动勾选一堆权限;离职时一键解绑角色即可回收权限。
这就叫—— 职责分离 + 最小权限原则 ✅
好了,理论铺垫完毕,咱们进入实战环节!
Spring Boot后端服务搭建:安全优先的设计哲学
现在开始搭架子。我们用 Spring Boot 作为后端框架,原因很简单:生态成熟、社区强大、整合方便,特别是配合 Spring Security 几乎是Java领域的事实标准。
不过别一上来就莽代码!好的系统一定是“设计先行”。我们在项目初期就要确立几个关键原则:
分层清晰,各司其职 💼
推荐使用经典的四层架构:
| 层级 | 职责 |
|---|---|
Controller |
处理HTTP请求,参数校验,返回统一响应 |
Service |
封装核心业务逻辑,协调多个DAO操作 |
Mapper/Repository |
数据访问层,执行SQL或JPA查询 |
Entity/DTO |
领域对象与传输对象 |
每一层只跟相邻层打交道,形成清晰的调用链条。这样做的好处是:后期改需求、加日志、做单元测试都特别方便。
来看一段真实的控制器代码:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthService authService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
String token = authService.authenticate(request.getUsername(), request.getPassword());
return ResponseEntity.ok(Map.of("token", token));
}
}
注意看,这里 AuthController 只做了三件事:
1. 接收 /api/auth/login 的 POST 请求;
2. 把账号密码转发给 AuthService ;
3. 返回一个带JWT令牌的JSON。
真正的认证逻辑藏在 authService.authenticate() 里面,外面根本看不到细节。这就是所谓的“瘦控制器”原则—— Controller 不要写太多逻辑,否则会变得难以测试和维护 。
整个调用流程可以用一张图表示:
graph TD
A[客户端] --> B[AuthController]
B --> C[AuthService]
C --> D[UserDetailsService]
C --> E[JwtUtil]
D --> F[UserMapper]
F --> G[(MySQL)]
每一层像积木一样堆上去,松耦合、易替换。比如将来想换Redis存储用户信息?只需要改 UserMapper 实现类,上层完全不用动!
高内聚低耦合,模块化设计 🧩
为了进一步提升可维护性,我们还要遵守SOLID原则中的两条黄金法则:
- 单一职责 :每个类只干一件事;
- 依赖倒置 :面向接口编程,而不是具体实现。
拿角色权限服务举例:
// 定义接口
public interface RoleService {
List<Role> getAllRoles();
void assignPermissionsToRole(Long roleId, List<Long> permissionIds);
}
// 具体实现
@Service
@Transactional
public class RoleServiceImpl implements RoleService {
@Autowired
private RoleMapper roleMapper;
@Autowired
private PermissionMapper permissionMapper;
@Override
public List<Role> getAllRoles() {
return roleMapper.selectAll();
}
@Override
@CacheEvict(value = "permissions", key = "#roleId")
public void assignPermissionsToRole(Long roleId, List<Long> permissionIds) {
roleMapper.deleteRolePermissions(roleId);
if (permissionIds != null && !permissionIds.isEmpty()) {
roleMapper.insertRolePermissions(roleId, permissionIds);
}
}
}
亮点在哪?
@Service让Spring自动托管这个Bean;@Transactional保证“删旧权限 + 插新权限”是一个原子操作,失败自动回滚;@CacheEvict在修改权限后清除Redis缓存,避免脏数据;- 所有依赖通过
@Autowired注入,没有new XXX()这种硬编码。
未来如果要支持远程调用(比如Feign),只要新增一个 RemoteRoleService 实现类,注入时切换实现即可,调用方无感知。
这才是真正意义上的 可扩展架构 !
安全是底线,必须前置考虑 🔐
权限系统本身就是安全设施,如果它自己都不安全,那岂不是笑话?
所以在设计阶段就得堵住常见漏洞:
✅ 输入验证不能少
所有外部传参都要校验,建议用 @Valid + Hibernate Validator:
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest req) {
// ...
}
并在 DTO 中加上约束注解:
public class RegisterRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 4, max = 20, message = "用户名长度应在4-20之间")
private String username;
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$",
message = "密码需包含大小写字母和数字,至少8位")
private String password;
}
✅ 敏感字段绝不暴露
数据库里的密码是加密存的,但在内存中也不能随便传递明文啊!
定义专门的传输对象(DTO),过滤掉敏感字段:
public class UserDto {
private Long id;
private String username;
private String email;
private List<String> roles; // 不包含password!
public static UserDto from(User user, List<String> roles) {
UserDto dto = new UserDto();
dto.setId(user.getId());
dto.setUsername(user.getUsername());
dto.setEmail(user.getEmail());
dto.setRoles(roles);
return dto;
}
}
你看,连getter/setter都没给password留位置,彻底杜绝泄露可能。
✅ 统一异常处理,别把堆栈甩给前端
曾经有个项目因为没处理好异常,导致攻击者通过错误信息猜出了表结构……血的教训!
用 @ControllerAdvice 全局拦截异常:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(Exception e) {
return ResponseEntity.badRequest().body(
new ErrorResponse("VALIDATION_ERROR", "参数校验失败")
);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponse> handleAuth(Exception e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(
new ErrorResponse("ACCESS_DENIED", "无权访问该资源")
);
}
}
这样一来,不管哪里出错,返回格式都是统一的 {code: "...", message: "..."} ,前端处理起来也省心。
讲完设计思想,下一步就是初始化项目了。推荐使用 https://start.spring.io 这个官方脚手架工具,选好依赖一键生成工程模板。
我们需要的核心依赖包括:
- Spring Web
- Spring Security
- MyBatis Framework
- MySQL Driver
- Lombok
- JWT 支持(手动添加)
生成后的目录结构长这样:
src/
├── main/
│ ├── java/
│ │ └── com/example/permissionsystem/
│ │ ├── PermissionSystemApplication.java
│ │ ├── controller/
│ │ ├── service/
│ │ ├── mapper/
│ │ └── config/
│ └── resources/
│ ├── application.yml
│ ├── mybatis-config.xml
│ └── mapper/*.xml
主启动类加上 @SpringBootApplication ,Spring Boot就会自动扫描包、加载配置、装配Bean,开箱即用 ⚡️
接下来重点来了:如何集成 JWT无状态认证 ?
传统Session需要服务器存状态,横向扩展困难;而JWT把用户信息编码进Token本身,服务端只负责签发和验证,非常适合微服务架构。
先引入依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
然后写个工具类生成和解析Token:
@Component
public class JwtUtil {
private final String SECRET_KEY = "your-secret-key-change-in-production"; // 生产环境务必更换!
private final long EXPIRATION_TIME = 864_000_000; // 10天
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
public String getUsernameFromToken(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
}
private boolean isTokenExpired(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration().before(new Date());
}
}
解释一下关键点:
- generateToken 创建一个包含用户名、签发时间、过期时间和签名的JWT;
- validateToken 检查Token是否属于当前用户且未过期;
- getUsernameFromToken 解析Token获取用户名,用于后续权限比对。
这个工具会在登录成功后调用,返回给前端一个形如 eyJhbGciOiJIUzUxMiJ9.xxxxx 的字符串,后续每次请求都放在 Authorization: Bearer <token> 头里传回来。
最后别忘了配置多环境支持!开发、测试、生产应该用不同的数据库和安全策略。
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:mysql://localhost:3306/perm_dev?useSSL=false&serverTimezone=UTC
username: root
password: root
---
spring:
config:
activate:
on-profile: prod
datasource:
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASS}
security:
oauth2:
client:
registration:
github:
clientId: ${GITHUB_CLIENT_ID}
clientSecret: ${GITHUB_CLIENT_SECRET}
通过 spring.profiles.active=prod 切换环境,生产环境的所有敏感配置都从环境变量注入,避免代码泄露风险。
flowchart LR
A[开发者本地] -->|dev配置| B(Spring Boot App)
C[测试服务器] -->|staging配置| B
D[生产集群] -->|prod配置 + 环境变量| B
一套代码,多套部署,DevOps效率直接起飞 🚀
数据持久层设计:MyBatis搞定复杂权限查询
有了后端骨架,接下来得把数据模型定下来。毕竟权限系统本质上是在管理“谁有什么权限”的关系网。
我们继续采用 RBAC 模型 ,核心实体就四个: 用户、角色、权限、资源 。它们之间的关系如下图所示:
erDiagram
sys_user ||--o{ sys_user_role : "has"
sys_role ||--o{ sys_user_role : "assigned_to"
sys_role ||--o{ sys_role_permission : "contains"
sys_permission ||--o{ sys_role_permission : "granted_to"
sys_user {
bigint id PK
varchar username
varchar password
varchar nickname
varchar email
tinyint status
datetime create_time
datetime update_time
}
sys_role {
bigint id PK
varchar role_code
varchar role_name
varchar description
tinyint status
datetime create_time
datetime update_time
}
sys_permission {
bigint id PK
varchar perm_code
varchar perm_name
varchar resource_type
varchar url_path
varchar method
bigint parent_id
int sort_order
tinyint status
datetime create_time
datetime update_time
}
sys_user_role {
bigint user_id FK
bigint role_id FK
}
sys_role_permission {
bigint role_id FK
bigint permission_id FK
}
看到了吗?用户和角色是多对多,角色和权限也是多对多,中间靠两张关联表连接。这种设计最大的好处是—— 权限变更不影响用户表结构 ,扩展性强!
再来看看权限表的具体字段该怎么设计。我们要支持菜单、按钮、API三种资源类型,所以得有个通用的 sys_permission 表:
| 字段名 | 类型 | 说明 |
|---|---|---|
id |
BIGINT | 主键 |
perm_code |
VARCHAR(64) | 权限唯一编码,如 user:add |
perm_name |
VARCHAR(50) | 显示名称 |
resource_type |
ENUM(‘menu’,’button’,’api’) | 资源类型 |
url_path |
VARCHAR(255) | 对应路径 |
method |
VARCHAR(10) | HTTP方法(仅API) |
parent_id |
BIGINT | 父节点ID,用于树形结构 |
sort_order |
INT | 排序权重 |
举个实际例子:
- 用户管理(菜单)
|- 用户列表(菜单)
|- 查询用户(按钮)
|- 新增用户(按钮)
|- 删除用户(API POST /api/users/delete)
对应的数据库记录:
| id | perm_code | perm_name | resource_type | url_path | method | parent_id |
|---|---|---|---|---|---|---|
| 1 | menu:user | 用户管理 | menu | /user | NULL | NULL |
| 2 | menu:user:list | 用户列表 | menu | /user/list | NULL | 1 |
| 3 | btn:user:query | 查询用户 | button | NULL | NULL | 2 |
| 4 | btn:user:add | 新增用户 | button | NULL | NULL | 2 |
| 5 | api:user:delete | 删除用户 | api | /api/users/delete | POST | 2 |
前端可以根据 perm_code 控制按钮显隐,后端可以用 AOP 拦截 @RequiresPermission("api:user:delete") 方法调用,做到 前后端权限双保险 !
中间表的设计也有讲究。比如 sys_user_role 应该这样建:
CREATE TABLE sys_user_role (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES sys_user(id) ON DELETE CASCADE,
FOREIGN KEY (role_id) REFERENCES sys_role(id) ON DELETE CASCADE,
INDEX idx_role_id (role_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键点:
- 使用 (user_id, role_id) 作为联合主键,节省空间;
- 添加外键约束,确保引用完整性;
- 单独为 role_id 建索引,方便查“某个角色下有哪些用户”。
高并发下还要注意事务一致性。比如批量分配角色的操作:
@Service
@Transactional
public class UserRoleService {
@Autowired
private UserRoleMapper userRoleMapper;
public void assignRolesToUser(Long userId, List<Long> roleIds) {
userRoleMapper.deleteByUserId(userId); // 清空旧权限
if (!CollectionUtils.isEmpty(roleIds)) {
userRoleMapper.batchInsert(userId, roleIds); // 批量插入
}
}
}
配合 XML 中的 <foreach> 实现高效批量插入:
<insert id="batchInsert">
INSERT INTO sys_user_role (user_id, role_id)
VALUES
<foreach collection="roleIds" item="roleId" separator=",">
(#{userId}, #{roleId})
</foreach>
</insert>
这一招能让性能提升几十倍,尤其适合一次性导入上百个用户的场景。
至于 ORM 框架,我们选择 MyBatis-Plus ,因为它不仅保留了原生 MyBatis 对 SQL 的完全掌控力,还提供了大量便捷功能:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
配置完 application.yml 后,写个实体类:
@Data
@TableName("sys_user")
public class SysUser {
private Long id;
private String username;
private String password;
private LocalDateTime createTime;
}
Mapper 接口继承 BaseMapper<SysUser> 就自动拥有了 selectById , insert , updateById 等常用方法:
public interface UserMapper extends BaseMapper<SysUser> {
}
复杂查询怎么办?比如要一次性查出用户+角色+权限信息,就得写 JOIN SQL 并配置 ResultMap :
<resultMap id="UserWithRolesAndPermissions" type="UserDetailDTO">
<id property="userId" column="id"/>
<result property="username" column="username"/>
<collection property="roles" ofType="string">
<result column="role_name"/>
</collection>
<collection property="permissions" ofType="string">
<result column="perm_code"/>
</collection>
</resultMap>
<select id="getUserDetailById" resultMap="UserWithRolesAndPermissions">
SELECT u.*, r.role_name, p.perm_code
FROM sys_user u
LEFT JOIN sys_user_role ur ON u.id = ur.user_id
LEFT JOIN sys_role r ON ur.role_id = r.id
LEFT JOIN sys_role_permission rp ON r.id = rp.role_id
LEFT JOIN sys_permission p ON rp.permission_id = p.id
WHERE u.id = #{userId}
</select>
这样一次查询就能拿到完整的权限视图,避免N+1问题,前端渲染菜单快如闪电 ⚡️
还有更酷的——动态SQL!比如按用户名、状态、创建时间组合筛选用户:
<select id="listUsers" parameterType="QueryUserReq" resultType="SysUser">
SELECT * FROM sys_user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
</where>
ORDER BY create_time DESC
</select>
<where> 标签会智能处理AND/OR开头的问题, <if> 实现条件拼接,再也不用手动拼字符串了,防SQL注入杠杠的!
Vue.js前端权限控制:让UI随权限而变
后端稳了,前端也不能掉链子。很多团队只做了接口权限,却忽略了页面元素的控制,结果出现了“按钮能点但接口报403”的尴尬局面。
我们的目标是: 用户看到的每一个按钮、每一条菜单,都是他真正有权操作的 。
为此,我们基于 Vue 3 + TypeScript + Pinia 搭建前端工程:
vue create permission-frontend
# 选择 Vue 3, TypeScript, Router, Pinia, Sass
目录结构清晰划分:
| 目录 | 用途 |
|---|---|
/src/views |
页面组件 |
/src/components |
通用UI组件 |
/src/router |
路由配置 |
/src/store |
状态管理 |
/src/directives |
自定义指令 |
/src/utils |
工具函数 |
动态路由:登录后才知能去哪 🗺️
传统静态路由所有人都能看到所有页面,存在安全隐患。我们必须改成 后端返回路由表 + 前端动态挂载 的模式。
假设后端 /api/user/routes 返回:
[
{
"path": "/dashboard",
"name": "Dashboard",
"component": "views/Dashboard.vue",
"meta": { "title": "仪表盘", "roles": ["admin", "user"] }
},
{
"path": "/user",
"name": "UserManagement",
"component": "views/User/List.vue",
"meta": { "title": "用户管理", "roles": ["admin"] }
}
]
前端收到后动态注册:
export const loadDynamicRoutes = async (router: any) => {
const res = await fetch('/api/user/routes', {
headers: { Authorization: `Bearer ${getToken()}` }
});
const routes: RouteRecordRaw[] = await res.json();
routes.forEach(route => {
router.addRoute({
...route,
component: () => import(`@/${route.component}`)
});
});
};
利用 import() 实现代码分割,只有访问时才加载对应模块,首屏更快!
同时借助 meta 字段携带权限标识:
meta: {
title: '系统设置',
roles: ['admin'],
permissions: ['system:config:edit']
}
这些元信息将在后续的守卫和菜单渲染中起决定作用。
流程图如下:
graph TD
A[用户登录] --> B{已登录?}
B -- 是 --> C[拉取专属路由表]
C --> D[转换component为异步导入]
D --> E[调用addRoute逐个注册]
E --> F[完成路由初始化]
F --> G[跳转首页]
B -- 否 --> H[跳转登录页]
自定义指令:一行代码控制按钮显隐 ✨
细粒度权限控制最常用的手段就是自定义指令:
// directives/permission.ts
const permissionDirective: Directive = {
mounted(el, binding) {
const requiredRoles = binding.value;
const hasPerm = usePermissionStore().hasRole(requiredRoles);
if (!hasPerm) {
el.parentNode?.removeChild(el); // 直接移除DOM,更安全
}
}
};
app.directive('permission', permissionDirective);
使用起来超级简单:
<button v-permission="['admin']">删除用户</button>
相比 v-if 或 v-show ,这种方式连元素都不会出现在DOM树里,从根本上杜绝了手动启用的可能性。
对于需要提示的场景,还可以封装一个 <auth-button> 组件:
<auth-button :perms="['user:create']" type="primary">新增</auth-button>
点击无权限按钮时弹Toast提醒,用户体验更好。
路由守卫:最后一道防线 🔒
即使前端渲染了正确菜单,用户仍可能手动输入URL访问非法页面。因此必须设置全局前置守卫:
router.beforeEach(async (to, from, next) => {
const whiteList = ['/login', '/403'];
const token = getToken();
if (!token && !whiteList.includes(to.path)) {
return next('/login');
}
if (!usePermStore().isLoaded) {
await usePermStore().fetchPermissions();
}
const requiredRoles = to.meta.roles;
if (requiredRoles && !usePermStore().hasRole(requiredRoles)) {
next('/403');
} else {
next();
}
});
配合友好的403页面:
<h1>403 - 访问被拒绝</h1>
<p>您没有权限查看此页面。</p>
<el-button @click="$router.push('/')">返回首页</el-button>
真正做到层层设防,滴水不漏!
实战演练:全流程打通权限分配
现在让我们走一遍完整的权限分配流程。
用户管理:密码安全是第一课 🔐
新增用户时,密码必须加密存储:
@Autowired
private PasswordEncoder passwordEncoder; // 默认是 BCryptPasswordEncoder
user.setPassword(passwordEncoder.encode(rawPassword));
BCrypt的优势在于:
- 每次加密结果都不同(盐值内置);
- 抗彩虹表攻击;
- 可调节强度(默认10轮哈希)。
密码重置建议走邮箱链接方式,链接带过期Token,点击后跳转修改页面。
角色绑定:可视化勾选权限 🎯
前端用 el-tree 展示权限树:
<el-tree
:data="permissionTree"
show-checkbox
node-key="id"
ref="tree">
</el-tree>
<button @click="save()">保存角色权限</button>
<script>
async save() {
const ids = this.$refs.tree.getCheckedKeys();
await api.post(`/roles/${roleId}/perms`, { permIds: ids });
}
</script>
后端接收并更新中间表:
@PutMapping("/roles/{roleId}/perms")
public void updateRolePerms(@PathVariable Long roleId, @RequestBody Set<Long> permIds) {
roleService.updatePermissions(roleId, permIds);
}
方法级权限:AOP自动拦截 ⚔️
定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
String value(); // 如 "user:delete"
}
结合AOP进行校验:
@Aspect
@Component
public class PermissionAspect {
@Around("@annotation(perm)")
public Object check(ProceedingJoinPoint pjp, RequiresPermission perm) {
String required = perm.value();
Long userId = getCurrentUserId();
if (!userHasPermission(userId, required)) {
throw new AccessDeniedException("权限不足");
}
return pjp.proceed();
}
}
使用时只需加个注解:
@RequiresPermission("user:delete")
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
干净利落,毫无侵入性!
审计日志:谁干了什么一清二楚 📜
每个敏感操作都要留痕:
@Log(action = "删除用户", module = "用户管理")
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) { ... }
AOP捕获并异步写入数据库:
@After("@annotation(log)")
public void record(JoinPoint jp, Log log) {
AuditLog auditLog = buildLog(jp, log);
logAsyncService.saveAsync(auditLog); // 异步落库,不影响主流程
}
日志表支持按时间、用户、模块检索,可用于安全审计和故障排查。
部署与热更新:不停机能改权限吗?🔄
当然可以!我们采用“Redis缓存 + 消息队列通知”的方案。
权限变更时:
@Autowired
private RedisTemplate<String, Set<String>> redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
public void updatePermissions(Long userId) {
// 更新Redis缓存
Set<String> newPerms = loadUserPermissions(userId);
redisTemplate.opsForValue().set("user:" + userId + ":perms", newPerms, Duration.ofHours(2));
// 广播消息
rabbitTemplate.convertAndSend("perm.exchange", "perm.updated", Map.of("userId", userId));
}
前端通过WebSocket监听事件:
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'PERMISSION_UPDATE' && data.userId === myId) {
location.reload(); // 或局部刷新菜单
}
};
实现 不重启服务、权限实时生效 的效果!
最后用Docker容器化部署:
FROM openjdk:17-jdk-alpine
COPY target/auth-service.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
配合CI/CD流水线,从提交代码到上线全自动完成,效率翻倍!
整套系统跑通之后你会发现:权限管理不再是负担,而是一种 赋能机制 。它让组织规则数字化,让安全防护自动化,让团队协作更顺畅。
而这,正是现代企业应用应有的样子 💪
简介:权限管理系统是保障信息安全与操作规范的核心机制,广泛应用于企业级IT系统中。本项目基于Spring Boot、MyBatis和Vue.js三大主流框架构建,涵盖用户管理、角色管理、资源分配、权限控制、审计日志及动态权限调整等核心功能。通过前后端分离架构,实现安全高效的权限控制体系。项目包含完整的设计文档、数据库脚本、源码与测试用例,适合开发者学习框架集成与企业级权限管理的实战应用。
更多推荐





所有评论(0)