22 KiB
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 | — |
设计模式
-
统一继承 BaseController:所有 Controller 继承
BaseController,获得分页、响应、用户上下文等通用能力。 -
标准 CRUD 接口模式:
GET /list → 分页列表查询 GET /{id} → 详情查询 POST / → 新增 PUT / → 修改 DELETE /{ids} → 批量删除 POST /export → Excel 导出 POST /importData → Excel 导入 -
权限注解驱动:每个接口通过
@PreAuthorize("@ss.hasPermi('module:entity:action')")进行权限控制。 -
操作日志注解:通过
@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:
| 标签 | 用途 | 示例 |
|---|---|---|
<if> |
条件查询 | <if test="userName != null and userName != ''"> |
<foreach> |
批量操作 | <foreach collection="array" item="userId"> |
<include> |
SQL 片段复用 | <include refid="selectUserVo"/> |
<sql> |
定义可复用片段 | <sql id="selectUserVo"> |
<set> |
动态更新 | <set><if test="...">field = #{...},</if></set> |
${params.dataScope} |
数据权限注入 | 由 DataScopeAspect 动态拼接 |
ResultMap 关联映射
使用 <association> 和 <collection> 实现一对多关联查询:
<association property="dept" javaType="SysDept" resultMap="deptResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
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/** |
| 其他所有请求 | 需要认证 |
方法级安全
@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 工作流程
- 登录 → 生成 UUID → 存入 Redis → JWT 携带 UUID → 返回 Token
- 请求 → 提取 Token → 解析 UUID → Redis 获取用户 → 验证有效期
- 刷新 → 距过期不足 20 分钟 → 自动续期 Redis TTL
- 登出 → 删除 Redis 中的用户缓存
2.3 权限控制体系
权限模型
用户 (SysUser)
└── 角色 (SysRole) [多对多]
└── 菜单/权限 (SysMenu) [多对多]
└── 权限标识: "system:user:list"
三种权限控制方式
| 方式 | 注解/实现 | 层级 | 用途 |
|---|---|---|---|
| URL 级 | Spring Security antMatchers |
网关 | 公开/匿名资源 |
| 方法级 | @PreAuthorize("@ss.hasPermi('...')") |
Controller | 功能权限 |
| 数据级 | @DataScope(deptAlias, userAlias) |
Service | 数据范围过滤 |
PermissionService 权限判断
@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 拼接示例:
-- 本部门权限
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),未来接口升级时无法平滑过渡 |
文档结束