# RuoYi-Vue 后端架构分析 > 版本:3.8.0 | 分析日期:2026-06-28 | 框架:Spring Boot 2.5.8 --- ## 1. 分层架构分析 ### 1.1 整体分层概览 项目采用经典的 **四层架构**:Controller → Service → Mapper → Domain,各层职责清晰,通过 Maven 多模块进行物理隔离。 ``` ┌─────────────────────────────────────────────────────────────┐ │ ruoyi-admin (启动层) │ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │ │ │ Controller │ │ Config │ │ Application │ │ │ │ (18个) │ │ (Swagger) │ │ RuoYiApplication │ │ │ └──────┬──────┘ └─────────────┘ └──────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ ruoyi-framework (框架层) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ │ │ │ │ Aspect │ │ Security │ │ Config │ │ Manager │ │ │ │ │ │ (4个切面)│ │ (JWT) │ │ (12个) │ │ (异步) │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ └─────────┘ │ │ │ └──────────────────────┬───────────────────────────────┘ │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ │ruoyi- │ │book- │ │ stock- │ │ │ │system │ │system │ │ system │ │ │ │(15 Mapper│ │(6 Mapper │ │ (9 Mapper │ │ │ │ 12 Service│ │ 6 Service│ │ ... │ │ │ │ 12 Domain)│ │ 6 Domain)│ │ │ │ │ └──────────┘ └──────────┘ └──────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ ruoyi-common (通用工具层) │ │ │ │ BaseController / AjaxResult / BaseEntity / 注解 / │ │ │ │ 工具类(20+) / 异常类 / 过滤器 / 常量 │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ### 1.2 Controller 层分析 **文件位置**:`ruoyi-admin/src/main/java/com/ruoyi/web/controller/` #### Controller 统计 | 分类 | 数量 | 文件列表 | |------|------|----------| | 系统管理 | 13 | SysConfigController, SysDeptController, SysDictDataController, SysDictTypeController, SysIndexController, SysLoginController, SysMenuController, SysNoticeController, SysPostController, SysProfileController, SysRegisterController, SysRoleController, SysUserController | | 监控管理 | 5 | CacheController, ServerController, SysLogininforController, SysOperlogController, SysUserOnlineController | | 业务模块 | 6 | AccountBookController, AccountController, OperationsController, StatisticsController, StatisticsRemainController, StatisticsTotalController | | 工具/公共 | 4 | CaptchaController, CommonController, SwaggerController, TestController | | **合计** | **28** | — | #### 设计模式 1. **统一继承 BaseController**:所有 Controller 继承 `BaseController`,获得分页、响应、用户上下文等通用能力。 2. **标准 CRUD 接口模式**: ``` GET /list → 分页列表查询 GET /{id} → 详情查询 POST / → 新增 PUT / → 修改 DELETE /{ids} → 批量删除 POST /export → Excel 导出 POST /importData → Excel 导入 ``` 3. **权限注解驱动**:每个接口通过 `@PreAuthorize("@ss.hasPermi('module:entity:action')")` 进行权限控制。 4. **操作日志注解**:通过 `@Log(title = "模块名", businessType = BusinessType.XXX)` 声明式记录操作日志。 ### 1.3 Service 层分析 **文件位置**: - 系统模块:`ruoyi-system/src/main/java/com/ruoyi/system/service/` - 账务模块:`book-system/src/main/java/com/ruoyi/booksystem/service/` #### 接口与实现分离 采用标准的 **接口 + 实现类** 模式: ``` ISysUserService (接口) └── SysUserServiceImpl (实现) ``` | 模块 | 接口数 | 实现类数 | |------|--------|----------| | ruoyi-system | 12 | 12 | | book-system | 6 | 6 | #### 事务管理 使用 `@Transactional` 注解管理事务,主要在以下场景使用: | 场景 | 方法示例 | |------|----------| | 新增用户(含角色/岗位关联) | `insertUser()` | | 修改用户(含角色/岗位关联) | `updateUser()` | | 删除用户(含关联清理) | `deleteUserById()`, `deleteUserByIds()` | | 用户授权角色 | `insertUserAuth()` | **事务策略特点**: - 仅在涉及多表操作的复合方法上加 `@Transactional` - 简单 CRUD(如 `updateUserStatus`)不使用事务 - 使用默认的 `REQUIRED` 传播级别和 `RuntimeException` 回滚策略 #### 数据权限注解 Service 层通过 `@DataScope(deptAlias = "d", userAlias = "u")` 注解实现数据权限过滤,在方法执行前由 AOP 切面动态注入 SQL 过滤条件。 ### 1.4 Mapper 层分析 **文件位置**: - XML 映射:`{module}/src/main/resources/mapper/{module}/` - Java 接口:`{module}/src/main/java/com/ruoyi/{module}/mapper/` #### MyBatis 映射文件统计 | 模块 | XML 文件数 | 主要功能 | |------|-----------|----------| | ruoyi-system | 11 | 用户、角色、菜单、部门、字典、岗位、日志、配置、公告 | | book-system | 6 | 账户、账本、操作、统计 | | stock-system | 9 | 股票、行情、财务、趋势 | | ruoyi-quartz | 2 | 定时任务 | | ruoyi-generator | 2 | 代码生成 | | **合计** | **30** | — | #### 动态 SQL 使用 以 `SysUserMapper.xml` 为例,大量使用 MyBatis 动态 SQL: | 标签 | 用途 | 示例 | |------|------|------| | `` | 条件查询 | `` | | `` | 批量操作 | `` | | `` | SQL 片段复用 | `` | | `` | 定义可复用片段 | `` | | `` | 动态更新 | `field = #{...},` | | `${params.dataScope}` | 数据权限注入 | 由 DataScopeAspect 动态拼接 | #### ResultMap 关联映射 使用 `` 和 `` 实现一对多关联查询: ```xml ``` ### 1.5 Domain 层分析 #### 基类体系 ``` BaseEntity (基础实体) ├── searchValue, createBy, createTime, updateBy, updateTime, remark, params │ ├── TreeEntity (树形实体) │ └── parentId, ancestors │ └── AjaxResult (统一响应) └── code, msg, data (继承 HashMap) ``` #### 核心实体类 | 实体 | 位置 | 用途 | |------|------|------| | SysUser | ruoyi-common | 用户信息 | | SysRole | ruoyi-common | 角色信息 | | SysMenu | ruoyi-common | 菜单/权限 | | SysDept | ruoyi-common | 部门信息 | | LoginUser | ruoyi-common | 登录用户上下文 | | SysConfig/SysNotice/SysOperLog | ruoyi-system | 系统业务实体 | | Account/AccountBook/Statistics | book-system | 账务业务实体 | --- ## 2. 安全配置 ### 2.1 Spring Security 配置 **配置文件**:`ruoyi-framework/.../config/SecurityConfig.java` #### 认证流程 ``` HTTP Request │ ▼ ┌──────────────────────┐ │ CorsFilter │ ← 跨域处理 └──────────┬───────────┘ ▼ ┌──────────────────────┐ │ JwtAuthentication │ ← JWT Token 验证 │ TokenFilter │ └──────────┬───────────┘ ▼ ┌──────────────────────┐ │ UsernamePassword │ ← Spring Security 内置 │ AuthenticationFilter │ └──────────┬───────────┘ ▼ ┌──────────────────────┐ │ Controller Handler │ └──────────────────────┘ ``` #### 安全配置要点 | 配置项 | 值/策略 | |--------|---------| | CSRF | 禁用(无状态 Token 认证) | | Session | STATELESS(无会话) | | 密码加密 | BCryptPasswordEncoder | | 匿名路径 | `/login`, `/register`, `/captchaImage` | | 公共资源 | `/**/*.html`, `/**/*.css`, `/**/*.js`, `/profile/**` | | Swagger | `/swagger-ui.html`, `/swagger-resources/**` | | 监控 | `/druid/**` | | 其他所有请求 | 需要认证 | #### 方法级安全 ```java @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) ``` - 启用 `@PreAuthorize` 和 `@PostAuthorize` 方法级权限控制 - 启用 `@Secured` 注解支持 ### 2.2 JWT Token 机制 **核心类**:`ruoyi-framework/.../web/service/TokenService.java` #### Token 结构 ``` JWT Header + Payload Redis 缓存 ┌─────────────────────┐ ┌──────────────────────┐ │ { │ │ login_tokens:{uuid} │ │ LOGIN_USER_KEY │──uuid──→ │ { │ │ : uuid │ │ LoginUser Object │ │ } │ │ TTL: 30min │ └─────────────────────┘ └──────────────────────┘ ``` #### 关键参数 | 参数 | 配置项 | 默认值 | |------|--------|--------| | Token Header | `token.header` | `Authorization` | | Token Secret | `token.secret` | 自定义密钥 | | Token 有效期 | `token.expireTime` | 30 分钟 | | 自动刷新阈值 | 硬编码 | 剩余不足 20 分钟时刷新 | #### Token 工作流程 1. **登录** → 生成 UUID → 存入 Redis → JWT 携带 UUID → 返回 Token 2. **请求** → 提取 Token → 解析 UUID → Redis 获取用户 → 验证有效期 3. **刷新** → 距过期不足 20 分钟 → 自动续期 Redis TTL 4. **登出** → 删除 Redis 中的用户缓存 ### 2.3 权限控制体系 #### 权限模型 ``` 用户 (SysUser) └── 角色 (SysRole) [多对多] └── 菜单/权限 (SysMenu) [多对多] └── 权限标识: "system:user:list" ``` #### 三种权限控制方式 | 方式 | 注解/实现 | 层级 | 用途 | |------|-----------|------|------| | URL 级 | Spring Security `antMatchers` | 网关 | 公开/匿名资源 | | 方法级 | `@PreAuthorize("@ss.hasPermi('...')")` | Controller | 功能权限 | | 数据级 | `@DataScope(deptAlias, userAlias)` | Service | 数据范围过滤 | #### PermissionService 权限判断 ```java @Service("ss") public class PermissionService { hasPermi("system:user:list") // 是否拥有某权限 lacksPermi("system:user:list") // 是否不拥有某权限 hasAnyPermi("a,b,c") // 是否拥有任一权限 hasRole("admin") // 是否拥有某角色 lacksRole("admin") // 是否不拥有某角色 hasAnyRoles("admin,common") // 是否拥有任一角色 } ``` 权限标识约定:`{模块}:{实体}:{操作}`,如 `system:user:list`、`booksystem:account:add`。 --- ## 3. AOP 切面分析 **文件位置**:`ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/` ### 3.1 切面总览 ``` ┌─────────────────────────────────────────────────────┐ │ AOP 切面 (4个) │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ LogAspect │ │ DataScope │ │ │ │ @AfterRet │ │ @Before │ │ │ │ @AfterThrow │ │ 数据权限过滤 │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ DataSource │ │ RateLimiter │ │ │ │ @Around │ │ @Before │ │ │ │ 多数据源切换 │ │ Redis 限流 │ │ │ │ Order(1) │ │ │ │ │ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────┘ ``` ### 3.2 日志切面 (LogAspect) | 属性 | 值 | |------|-----| | 触发方式 | `@annotation(Log)` | | 通知类型 | `@AfterReturning`(成功)、`@AfterThrowing`(异常) | | 记录内容 | 操作用户、IP、URL、方法名、请求参数、响应结果、业务类型、状态 | | 写入策略 | **异步写入**(AsyncManager + AsyncFactory),不阻塞主线程 | | 参数过滤 | 自动过滤 MultipartFile、HttpServletRequest 等不可序列化对象 | ### 3.3 数据权限切面 (DataScopeAspect) | 属性 | 值 | |------|-----| | 触发方式 | `@annotation(DataScope)` | | 通知类型 | `@Before` | | 权限级别 | 5 种:全部(1)、自定义(2)、本部门(3)、本部门及以下(4)、仅本人(5) | | 实现原理 | 在查询参数 `params.dataScope` 中注入 SQL 片段,XML 中通过 `${params.dataScope}` 拼接 | | 安全机制 | 执行前先清空 `dataScope` 参数防止 SQL 注入 | **SQL 拼接示例**: ```sql -- 本部门权限 AND (d.dept_id = 100) -- 本部门及以下 AND (d.dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = 100 OR find_in_set(100, ancestors))) ``` ### 3.4 数据源切面 (DataSourceAspect) | 属性 | 值 | |------|-----| | 触发方式 | `@annotation(DataSource)` 或 `@within(DataSource)` | | 通知类型 | `@Around` | | 优先级 | `@Order(1)`(最高优先级) | | 实现原理 | 通过 `ThreadLocal` 设置当前线程的数据源标识,执行完毕后清理 | | 底层支持 | `DynamicDataSource`(继承 `AbstractRoutingDataSource`) | ### 3.5 限流切面 (RateLimiterAspect) | 属性 | 值 | |------|-----| | 触发方式 | `@annotation(RateLimiter)` | | 通知类型 | `@Before` | | 存储介质 | Redis + Lua 脚本(原子操作) | | 限流维度 | 默认(方法级)、IP(IP + 方法级) | | 可配置参数 | `key`(键前缀)、`time`(时间窗口/秒)、`count`(最大请求数) | | 默认值 | 60 秒内最多 100 次请求 | | 失败响应 | 抛出 `ServiceException("访问过于频繁,请稍候再试")` | --- ## 4. 模块依赖 ### 4.1 模块依赖图 ``` ┌─────────────┐ │ ruoyi-admin │ ← 启动入口,打包部署 └──────┬──────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │ruoyi- │ │ruoyi- │ │ ruoyi- │ │system │ │quartz │ │ generator │ └─────┬─────┘ └─────┬─────┘ └──────┬───────┘ │ │ │ ▼ ▼ ▼ ┌──────────────────────────────────────────┐ │ ruoyi-framework │ │ (Security, AOP, DataSource, Config) │ └────────────────────┬─────────────────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │book- │ │stock- │ │ ruoyi- │ │system │ │system │ │ common │ └─────┬─────┘ └─────┬─────┘ └──────┬───────┘ │ │ │ └──────────────┼───────────────┘ ▼ (所有业务模块依赖) ``` ### 4.2 模块职责 | 模块 | 职责 | 核心内容 | |------|------|----------| | **ruoyi-admin** | 启动入口 & Controller 层 | SpringBoot 启动类、Controller、Swagger 配置 | | **ruoyi-framework** | 框架核心 | Security 配置、JWT、AOP 切面、数据源、Redis、MyBatis 配置 | | **ruoyi-system** | 系统管理业务 | 用户、角色、菜单、部门、字典、岗位、日志的 Service/Mapper/Domain | | **ruoyi-common** | 通用工具 | 基类、工具类、注解、常量、异常、过滤器、分页组件 | | **book-system** | 账务业务模块 | 账本、账户、操作记录、统计的 CRUD | | **stock-system** | 股票业务模块 | 股票信息、行情、财务、趋势分析 | | **ruoyi-quartz** | 定时任务 | 基于 Quartz 的任务调度 | | **ruoyi-generator** | 代码生成 | 基于 Velocity 模板的代码生成器 | ### 4.3 技术栈汇总 | 分类 | 技术 | 版本 | |------|------|------| | 核心框架 | Spring Boot | 2.5.8 | | JDK | Java | 1.8 | | 安全框架 | Spring Security + JWT | 内置 + jjwt 0.9.1 | | ORM | MyBatis | 2.2.0 (spring-boot-starter) | | 分页 | PageHelper | 1.4.0 | | 数据库连接池 | Druid | 1.2.8 | | 缓存 | Redis (Spring Data Redis) | 内置 | | API 文档 | Springfox Swagger 3 | 3.0.0 | | JSON | FastJSON | 1.2.79 | | Excel | Apache POI | 4.1.2 | | 验证码 | Kaptcha | 2.3.2 | | 代码生成 | Velocity | 2.3 | | 系统监控 | Oshi | 5.8.6 | --- ## 5. 存在的问题 | # | 问题 | 严重等级 | 影响范围 | 优化建议 | |---|------|----------|----------|----------| | 1 | **fastjson 版本存在已知漏洞** | 🔴 高 | 全系统 | fastjson 1.2.79 存在多个 CVE 漏洞(如 CVE-2022-25845),建议升级至 2.x(fastjson2)或替换为 Jackson/Gson | | 2 | **使用已废弃的 WebSecurityConfigurerAdapter** | 🟡 中 | 安全配置 | Spring Security 5.7+ 已废弃 `WebSecurityConfigurerAdapter`,应改用 `SecurityFilterChain` Bean 配置方式 | | 3 | **Token 解析异常被静默吞没** | 🟡 中 | 认证模块 | `TokenService.getLoginUser()` 中 catch 块为空(第 72-74 行),异常被完全忽略,建议至少记录 WARN 日志 | | 4 | **SQL 注入风险:${params.dataScope}** | 🟡 中 | 数据权限层 | MyBatis XML 中 `${params.dataScope}` 使用 `${}` 直接拼接,虽有 DataScopeAspect 前置清空防护,但仍存在绕过风险;建议使用参数化方案或严格校验 | | 5 | **业务 Controller 缺少 @Log 注解** | 🟢 低 | book-system | `book-system` 下的 Controller 中 `AccountController` 等部分接口缺少 `@Log` 操作日志注解,审计不完整 | | 6 | **缺少统一 DTO/VO 分层** | 🟢 低 | 全系统 | Domain 实体直接暴露给 Controller 层(如 `SysUser` 同时用于数据库映射和接口响应),密码等敏感字段需额外处理;建议引入独立的 DTO/VO 对象 | | 7 | **Service 层直接注入 Mapper(而非通过接口)** | 🟢 低 | 全系统 | `SysUserServiceImpl` 中使用 `@Autowired private SysUserMapper userMapper` 直接注入实现类,虽然 MyBatis 通过代理实现,但不符合面向接口编程原则 | | 8 | **缺少接口版本控制** | 🟢 低 | API 设计 | 所有 API 路径无版本号(如 `/api/v1/system/user`),未来接口升级时无法平滑过渡 | --- *文档结束*