# 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 result = userService.register(userDto); ``` #### 用户登录 ```java UserDto loginDto = new UserDto(); loginDto.setUsername("testuser"); loginDto.setPassword("Test123!"); RestBean 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 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 create(@RequestBody ArticleDto dto) { // ... } // 需要管理员角色或文章管理权限 @PreAuthorize("hasAnyRole('ROLE_ADMIN') or hasAuthority('article:delete')") @DeleteMapping("/{id}") public RestBean 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 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 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 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 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 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 { @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 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 - 定期审查用户权限 - 启用审计日志