Files
springboot-template/README_USAGE.md
shenjianZ 40c85c3c1f feat: 实现完整的 RBAC 权限管理系统与基础设施增强
在初始认证基础上,新增完整的 RBAC 权限模型(角色、权限、菜单三级管理),
  集成审计日志、接口限流、登录失败锁定、Refresh Token 机制、Redis 分布式缓存与锁、
  RocketMQ 消息队列,并引入 Flyway 数据库版本管理,同时补充项目文档与使用示例
2026-04-10 10:58:22 +08:00

560 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Spring Boot 模板项目使用文档
## 项目概述
这是一个功能完整的 Spring Boot 3.x 项目模板,集成了以下核心功能:
- **用户认证与授权**:基于 JWT + Spring Security
- **RBAC 权限模型**:角色-权限-菜单三级权限控制
- **数据库迁移**Flyway 版本化管理
- **Redis 缓存**:多种场景化缓存支持
- **RocketMQ 消息队列**:异步消息处理
- **分布式锁**:基于 Redis 实现
- **接口限流**:基于 Redis + AOP
- **审计日志**AOP 自动记录操作日志
- **账户安全**:登录失败锁定、密码强度校验
---
## 1. 快速开始
### 1.1 环境要求
- JDK 17+
- MySQL 8.0+
- Redis 6.0+
- RocketMQ 5.x可选
### 1.2 数据库初始化
项目使用 Flyway 自动管理数据库迁移,启动时会自动执行以下操作:
1. 创建用户表
2. 创建 RBAC 相关表(角色、权限、菜单)
3. 创建审计日志表
4. 创建 Refresh Token 表
### 1.3 配置文件
修改 `application-dev.yaml`
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=Asia/Shanghai
username: your_username
password: your_password
data:
redis:
host: localhost
port: 6379
password: your_redis_password
jwt:
secret: your-secret-key-at-least-64-characters-long
```
---
## 2. 核心功能使用
### 2.1 用户认证与授权
#### 注册用户
```java
@Autowired
private UserService userService;
UserDto userDto = new UserDto();
userDto.setUsername("testuser");
userDto.setPassword("Test123!"); // 必须包含大小写字母、数字、特殊字符
userDto.setEmail("test@example.com");
RestBean<LoginResponseVo> result = userService.register(userDto);
```
#### 用户登录
```java
UserDto loginDto = new UserDto();
loginDto.setUsername("testuser");
loginDto.setPassword("Test123!");
RestBean<LoginResponseVo> result = userService.login(loginDto);
// 返回 accessToken 和 refreshToken
```
#### 使用 Token 访问接口
```bash
curl -H "Authorization: Bearer {accessToken}" \
http://localhost:8080/api/v1/user/info
```
#### Token 刷新
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{"refreshToken": "{refreshToken}"}' \
http://localhost:8080/api/v1/user/refresh
```
#### 登出
```bash
curl -X POST \
-H "Authorization: Bearer {accessToken}" \
http://localhost:8080/api/v1/user/logout
```
---
### 2.2 RBAC 权限控制
#### 创建角色
```java
@Autowired
private SysRoleService roleService;
RoleDto roleDto = new RoleDto();
roleDto.setRoleCode("ROLE_EDITOR");
roleDto.setRoleName("编辑");
roleDto.setDescription("内容编辑角色");
roleDto.setSortOrder(3);
roleDto.setStatus(1);
RoleVo role = roleService.create(roleDto);
```
#### 为角色分配权限
```java
List<Long> permissionIds = Arrays.asList(1L, 2L, 3L); // 文章创建、编辑、删除
roleService.assignPermissions(roleId, permissionIds);
```
#### 为用户分配角色
```java
@Autowired
private UserService userService;
UserRoleUpdateDto dto = new UserRoleUpdateDto();
dto.setRoleIds(Set.of(1L, 2L)); // 角色ID列表
userService.updateUserRole(userId, dto);
```
#### 在 Controller 中使用权限注解
```java
@RestController
@RequestMapping("/api/v1/articles")
public class ArticleController {
// 只有具有 'article:create' 权限的用户可以访问
@PreAuthorize("hasAuthority('article:create')")
@PostMapping
public RestBean<Void> create(@RequestBody ArticleDto dto) {
// ...
}
// 需要管理员角色或文章管理权限
@PreAuthorize("hasAnyRole('ROLE_ADMIN') or hasAuthority('article:delete')")
@DeleteMapping("/{id}")
public RestBean<Void> delete(@PathVariable Long id) {
// ...
}
}
```
---
### 2.3 Redis 缓存使用
#### 基本缓存操作
```java
@Autowired
private RedisCache redisCache;
// 设置缓存30分钟过期
redisCache.set("user:" + userId, userObject, 30, TimeUnit.MINUTES);
// 获取缓存
User user = redisCache.get("user:" + userId, User.class);
// 删除缓存
redisCache.delete("user:" + userId);
```
#### 计数器(阅读数、点赞数)
```java
// 原子自增
long viewCount = redisCache.increment("article:view:" + articleId, 1);
// 原子自减
long stock = redisCache.decrement("product:stock:" + productId, quantity);
```
#### 哈希表(对象字段缓存)
```java
// 设置单个字段
redisCache.hSet("user:profile:" + userId, "nickname", "张三");
redisCache.hSet("user:profile:" + userId, "age", 25);
// 获取单个字段
String nickname = (String) redisCache.hGet("user:profile:" + userId, "nickname");
// 获取所有字段
Map<Object, Object> profile = redisCache.hGetAll("user:profile:" + userId);
```
#### 列表(消息队列)
```java
// 从右侧推入(队列尾部)
redisCache.lRightPush("queue:email", emailObject);
// 从左侧弹出(队列头部)
Object email = redisCache.lLeftPop("queue:email");
```
#### 集合(去重、标签)
```java
// 添加标签(自动去重)
redisCache.sAdd("article:tags:" + articleId, "Java", "Spring", "Redis");
// 检查标签是否存在
boolean hasTag = redisCache.sIsMember("article:tags:" + articleId, "Java");
// 获取所有标签
Set<Object> tags = redisCache.sMembers("article:tags:" + articleId);
```
#### 有序集合(排行榜)
```java
// 添加到排行榜
redisCache.zAdd("leaderboard:user:score", userId, score);
// 获取用户排名
Long rank = redisCache.zReverseRank("leaderboard:user:score", userId);
// 获取用户分数
Double score = redisCache.zScore("leaderboard:user:score", userId);
// 增加分数
redisCache.zIncrementScore("leaderboard:user:score", userId, 10.0);
```
---
### 2.4 分布式锁使用
```java
@Autowired
private RedisLock redisLock;
public void processOrder(Long orderId) {
// 1. 获取锁30秒过期
String lockValue = redisLock.tryLock("order:" + orderId, 30);
if (lockValue == null) {
throw new RuntimeException("订单正在处理中");
}
try {
// 2. 执行业务逻辑
// ...
} finally {
// 3. 释放锁(只有持有者才能释放)
redisLock.unlock("order:" + orderId, lockValue);
}
}
```
---
### 2.5 接口限流使用
```java
@RestController
public class LoginController {
// 限制每 IP 每分钟最多 5 次登录尝试
@RateLimit(permits = 5, seconds = 60, limitType = RateLimit.LimitType.IP)
@PostMapping("/login")
public RestBean<LoginResponseVo> login(@RequestBody UserDto dto) {
// ...
}
}
```
---
### 2.6 审计日志使用
```java
@RestController
public class UserController {
// 自动记录审计日志
@AuditLog(action = "update", resource = "user", description = "更新用户信息")
@PutMapping("/{id}")
@PreAuthorize("hasAuthority('user:update')")
public RestBean<UserVo> updateUser(@PathVariable Long id, @RequestBody UserDto dto) {
// 操作会被自动记录到 sys_audit_log 表
}
}
```
---
### 2.7 事务使用
#### 基本事务
```java
@Service
public class UserService {
// 所有异常都回滚
@Transactional(rollbackFor = Exception.class)
public void createUserWithRole(UserDto userDto, Set<Long> roleIds) {
// 创建用户
User user = new User();
// ...
userRepository.save(user);
// 分配角色
for (Long roleId : roleIds) {
SysRole role = roleRepository.findById(roleId)
.orElseThrow(() -> new BusinessException("角色不存在"));
user.getRoles().add(role);
}
userRepository.save(user);
// 任何异常都会回滚整个事务
}
}
```
#### 嵌套事务
```java
// 外层事务
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
// 业务逻辑
innerMethod(); // 加入外层事务
}
// 内层事务
@Transactional(propagation = Propagation.REQUIRED)
public void innerMethod() {
// 加入外层事务
}
```
#### 独立事务
```java
@Transactional(propagation = Propagation.REQUIRED)
public void mainMethod() {
// 主事务逻辑
// 独立事务(即使主事务回滚也不影响)
recordLog();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordLog() {
// 独立事务
}
```
---
### 2.8 RocketMQ 消息队列使用
#### 发送消息
```java
@Autowired
private UserMessageProducer messageProducer;
// 发送注册消息
messageProducer.sendRegisterMessage(userId, username, email);
// 发送登录消息
messageProducer.sendLoginMessage(userId, username, ipAddress);
```
#### 消费消息
消息会被 `UserMessageConsumer` 自动消费,根据消息类型执行不同操作:
```java
@RocketMQMessageListener(
consumerGroup = "user-consumer-group",
topic = "user-topic"
)
public class UserMessageConsumer implements RocketMQListener<UserMessage> {
@Override
public void onMessage(UserMessage message) {
switch (message.getMessageType()) {
case "REGISTER":
// 处理注册消息(发送欢迎邮件等)
break;
case "LOGIN":
// 处理登录消息(记录登录日志)
break;
// ...
}
}
}
```
---
## 3. 配置说明
### 3.1 JWT 配置
```yaml
jwt:
# JWT 密钥至少64字符
secret: your-secret-key-at-least-64-characters-long
# Access Token 过期时间(秒)
access-token-expiration: 3600 # 1小时
# Refresh Token 过期时间(秒)
refresh-token-expiration: 604800 # 7天
```
### 3.2 登录安全配置
```yaml
app:
login:
# 最大失败次数
max-attempts: 5
# 锁定时长(分钟)
lock-duration-minutes: 30
```
### 3.3 Redis 配置
```yaml
spring:
data:
redis:
host: localhost
port: 6379
password: your-password
database: 0
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 2
```
### 3.4 RocketMQ 配置
```yaml
rocketmq:
name-server: localhost:9876
producer:
group: user-producer-group
user-topic: user-topic
```
---
## 4. 常见问题
### 4.1 如何添加新的权限?
1. 在数据库中插入权限记录(通过 Flyway 迁移脚本)
2. 在 Controller 上使用 `@PreAuthorize` 注解
```java
@PreAuthorize("hasAuthority('new:permission')")
public void someMethod() { }
```
### 4.2 如何自定义限流规则?
使用 `@RateLimit` 注解:
```java
@RateLimit(
permits = 10, // 10次
seconds = 60, // 每分钟
limitType = RateLimit.LimitType.USER, // 按用户限流
keyPrefix = "custom:" // 自定义键前缀
)
public void customMethod() { }
```
### 4.3 如何实现缓存预热?
在应用启动时加载热点数据:
```java
@Component
public class CacheWarmupRunner implements ApplicationRunner {
@Autowired
private RedisCache redisCache;
@Override
public void run(ApplicationArguments args) {
// 预热热点数据
List<User> hotUsers = userRepository.findHotUsers();
for (User user : hotUsers) {
redisCache.set("user:" + user.getId(), user, 1, TimeUnit.HOURS);
}
}
}
```
---
## 5. 最佳实践
### 5.1 密码安全
- 生产环境必须修改 JWT 密钥
- 使用强密码策略(已集成 `@StrongPassword`
- 定期更换密码
### 5.2 数据库事务
- 查询操作使用 `@Transactional(readOnly = true)`
- 明确指定 `rollbackFor = Exception.class`
- 避免大事务
### 5.3 Redis 使用
- 合理设置过期时间,避免内存溢出
- 使用 Redis 分布式锁防止并发问题
- 热点数据使用缓存,减少数据库压力
### 5.4 安全建议
- 修改 CORS 配置,限制允许的域名
- 使用 HTTPS
- 定期审查用户权限
- 启用审计日志