ai-engineering-init 1.3.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/.claude/hooks/skill-forced-eval.js +4 -1
  2. package/.claude/settings.json +3 -3
  3. package/.claude/skills/add-skill/SKILL.md +252 -116
  4. package/.claude/skills/api-development/SKILL.md +83 -377
  5. package/.claude/skills/architecture-design/SKILL.md +138 -632
  6. package/.claude/skills/backend-annotations/SKILL.md +134 -506
  7. package/.claude/skills/banana-image/SKILL.md +10 -3
  8. package/.claude/skills/brainstorm/SKILL.md +103 -535
  9. package/.claude/skills/bug-detective/SKILL.md +147 -1097
  10. package/.claude/skills/bug-detective/references/error-patterns.md +242 -0
  11. package/.claude/skills/code-patterns/SKILL.md +116 -426
  12. package/.claude/skills/code-patterns/references/leniu-code-patterns.md +87 -0
  13. package/.claude/skills/crud-development/SKILL.md +64 -304
  14. package/.claude/skills/data-permission/SKILL.md +105 -412
  15. package/.claude/skills/data-permission/references/custom-data-scope.md +90 -0
  16. package/.claude/skills/file-oss-management/SKILL.md +106 -714
  17. package/.claude/skills/file-oss-management/references/entities.md +105 -0
  18. package/.claude/skills/file-oss-management/references/service-impl.md +104 -0
  19. package/.claude/skills/leniu-api-development/SKILL.md +142 -626
  20. package/.claude/skills/leniu-api-development/references/real-examples.md +273 -0
  21. package/.claude/skills/leniu-architecture-design/SKILL.md +176 -391
  22. package/.claude/skills/leniu-backend-annotations/SKILL.md +132 -519
  23. package/.claude/skills/leniu-brainstorm/SKILL.md +132 -541
  24. package/.claude/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
  25. package/.claude/skills/leniu-crud-development/SKILL.md +232 -938
  26. package/.claude/skills/leniu-crud-development/references/templates.md +597 -0
  27. package/.claude/skills/leniu-customization-location/SKILL.md +410 -0
  28. package/.claude/skills/leniu-data-permission/SKILL.md +70 -0
  29. package/.claude/skills/leniu-java-entity/SKILL.md +76 -590
  30. package/.claude/skills/leniu-java-entity/references/templates.md +237 -0
  31. package/.claude/skills/leniu-java-export/SKILL.md +94 -379
  32. package/.claude/skills/leniu-java-logging/SKILL.md +106 -709
  33. package/.claude/skills/leniu-java-logging/references/data-mask.md +46 -0
  34. package/.claude/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
  35. package/.claude/skills/leniu-java-mybatis/SKILL.md +73 -446
  36. package/.claude/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
  37. package/.claude/skills/leniu-report-customization/SKILL.md +111 -325
  38. package/.claude/skills/leniu-report-customization/references/table-fields.md +93 -0
  39. package/.claude/skills/leniu-report-standard-customization/SKILL.md +328 -0
  40. package/.claude/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
  41. package/.claude/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
  42. package/.claude/skills/leniu-security-guard/SKILL.md +133 -347
  43. package/.claude/skills/mysql-debug/SKILL.md +364 -0
  44. package/.claude/skills/openspec-apply-change/SKILL.md +10 -1
  45. package/.claude/skills/openspec-archive-change/SKILL.md +9 -1
  46. package/.claude/skills/openspec-bulk-archive-change/SKILL.md +9 -1
  47. package/.claude/skills/openspec-continue-change/SKILL.md +9 -1
  48. package/.claude/skills/openspec-explore/SKILL.md +10 -1
  49. package/.claude/skills/openspec-ff-change/SKILL.md +9 -1
  50. package/.claude/skills/openspec-new-change/SKILL.md +9 -1
  51. package/.claude/skills/openspec-onboard/SKILL.md +15 -130
  52. package/.claude/skills/openspec-sync-specs/SKILL.md +9 -1
  53. package/.claude/skills/openspec-verify-change/SKILL.md +9 -1
  54. package/.claude/skills/performance-doctor/SKILL.md +110 -434
  55. package/.claude/skills/redis-cache/SKILL.md +89 -595
  56. package/.claude/skills/redis-cache/references/listeners.md +23 -0
  57. package/.claude/skills/scheduled-jobs/SKILL.md +88 -407
  58. package/.claude/skills/security-guard/SKILL.md +137 -532
  59. package/.claude/skills/security-guard/references/encrypt-config.md +103 -0
  60. package/.claude/skills/security-guard/references/sensitive-strategies.md +42 -0
  61. package/.claude/skills/sms-mail/SKILL.md +116 -574
  62. package/.claude/skills/sms-mail/references/mail-config.md +88 -0
  63. package/.claude/skills/sms-mail/references/sms-config.md +74 -0
  64. package/.claude/skills/social-login/SKILL.md +112 -514
  65. package/.claude/skills/social-login/references/provider-configs.md +118 -0
  66. package/.claude/skills/tenant-management/SKILL.md +129 -444
  67. package/.claude/skills/tenant-management/references/tenant-scenarios.md +91 -0
  68. package/.claude/skills/test-development/SKILL.md +86 -540
  69. package/.claude/skills/test-development/references/parameterized-examples.md +119 -0
  70. package/.claude/skills/utils-toolkit/SKILL.md +52 -305
  71. package/.claude/skills/utils-toolkit/references/redis-utils-api.md +56 -0
  72. package/.claude/skills/websocket-sse/SKILL.md +105 -550
  73. package/.claude/skills/workflow-engine/SKILL.md +147 -502
  74. package/.codex/skills/add-skill/SKILL.md +252 -116
  75. package/.codex/skills/api-development/SKILL.md +172 -599
  76. package/.codex/skills/architecture-design/SKILL.md +138 -504
  77. package/.codex/skills/backend-annotations/SKILL.md +134 -496
  78. package/.codex/skills/banana-image/SKILL.md +10 -3
  79. package/.codex/skills/brainstorm/SKILL.md +103 -535
  80. package/.codex/skills/bug-detective/SKILL.md +147 -1097
  81. package/.codex/skills/bug-detective/references/error-patterns.md +242 -0
  82. package/.codex/skills/code-patterns/SKILL.md +120 -282
  83. package/.codex/skills/code-patterns/references/leniu-code-patterns.md +87 -0
  84. package/.codex/skills/crud-development/SKILL.md +64 -292
  85. package/.codex/skills/data-permission/SKILL.md +108 -407
  86. package/.codex/skills/data-permission/references/custom-data-scope.md +90 -0
  87. package/.codex/skills/database-ops/SKILL.md +8 -154
  88. package/.codex/skills/error-handler/SKILL.md +10 -0
  89. package/.codex/skills/file-oss-management/SKILL.md +106 -714
  90. package/.codex/skills/file-oss-management/references/entities.md +105 -0
  91. package/.codex/skills/file-oss-management/references/service-impl.md +104 -0
  92. package/.codex/skills/git-workflow/SKILL.md +27 -5
  93. package/.codex/skills/leniu-api-development/SKILL.md +142 -626
  94. package/.codex/skills/leniu-api-development/references/real-examples.md +273 -0
  95. package/.codex/skills/leniu-architecture-design/SKILL.md +176 -391
  96. package/.codex/skills/leniu-backend-annotations/SKILL.md +132 -519
  97. package/.codex/skills/leniu-brainstorm/SKILL.md +132 -541
  98. package/.codex/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
  99. package/.codex/skills/leniu-crud-development/SKILL.md +232 -938
  100. package/.codex/skills/leniu-crud-development/references/templates.md +597 -0
  101. package/.codex/skills/leniu-customization-location/SKILL.md +410 -0
  102. package/.codex/skills/leniu-data-permission/SKILL.md +70 -0
  103. package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
  104. package/.codex/skills/leniu-java-entity/SKILL.md +76 -590
  105. package/.codex/skills/leniu-java-entity/references/templates.md +237 -0
  106. package/.codex/skills/leniu-java-export/SKILL.md +94 -379
  107. package/.codex/skills/leniu-java-logging/SKILL.md +106 -709
  108. package/.codex/skills/leniu-java-logging/references/data-mask.md +46 -0
  109. package/.codex/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
  110. package/.codex/skills/leniu-java-mybatis/SKILL.md +73 -446
  111. package/.codex/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
  112. package/.codex/skills/leniu-report-customization/SKILL.md +111 -325
  113. package/.codex/skills/leniu-report-customization/references/table-fields.md +93 -0
  114. package/.codex/skills/leniu-report-standard-customization/SKILL.md +328 -0
  115. package/.codex/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
  116. package/.codex/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
  117. package/.codex/skills/leniu-security-guard/SKILL.md +133 -347
  118. package/.codex/skills/mysql-debug/SKILL.md +364 -0
  119. package/.codex/skills/openspec-apply-change/SKILL.md +10 -1
  120. package/.codex/skills/openspec-archive-change/SKILL.md +9 -1
  121. package/.codex/skills/openspec-bulk-archive-change/SKILL.md +9 -1
  122. package/.codex/skills/openspec-continue-change/SKILL.md +9 -1
  123. package/.codex/skills/openspec-explore/SKILL.md +10 -1
  124. package/.codex/skills/openspec-ff-change/SKILL.md +9 -1
  125. package/.codex/skills/openspec-new-change/SKILL.md +9 -1
  126. package/.codex/skills/openspec-onboard/SKILL.md +15 -130
  127. package/.codex/skills/openspec-sync-specs/SKILL.md +9 -1
  128. package/.codex/skills/openspec-verify-change/SKILL.md +9 -1
  129. package/.codex/skills/performance-doctor/SKILL.md +110 -434
  130. package/.codex/skills/project-navigator/SKILL.md +20 -1
  131. package/.codex/skills/redis-cache/SKILL.md +93 -589
  132. package/.codex/skills/redis-cache/references/listeners.md +23 -0
  133. package/.codex/skills/scheduled-jobs/SKILL.md +88 -407
  134. package/.codex/skills/security-guard/SKILL.md +141 -527
  135. package/.codex/skills/security-guard/references/encrypt-config.md +103 -0
  136. package/.codex/skills/security-guard/references/sensitive-strategies.md +42 -0
  137. package/.codex/skills/sms-mail/SKILL.md +116 -574
  138. package/.codex/skills/sms-mail/references/mail-config.md +88 -0
  139. package/.codex/skills/sms-mail/references/sms-config.md +74 -0
  140. package/.codex/skills/social-login/SKILL.md +112 -514
  141. package/.codex/skills/social-login/references/provider-configs.md +118 -0
  142. package/.codex/skills/store-pc/SKILL.md +258 -383
  143. package/.codex/skills/tenant-management/SKILL.md +129 -444
  144. package/.codex/skills/tenant-management/references/tenant-scenarios.md +91 -0
  145. package/.codex/skills/test-development/SKILL.md +86 -540
  146. package/.codex/skills/test-development/references/parameterized-examples.md +119 -0
  147. package/.codex/skills/ui-pc/SKILL.md +350 -387
  148. package/.codex/skills/utils-toolkit/SKILL.md +52 -283
  149. package/.codex/skills/utils-toolkit/references/redis-utils-api.md +56 -0
  150. package/.codex/skills/websocket-sse/SKILL.md +105 -550
  151. package/.codex/skills/workflow-engine/SKILL.md +147 -502
  152. package/.cursor/hooks.json +3 -3
  153. package/.cursor/rules/skill-activation.mdc +2 -0
  154. package/.cursor/skills/add-skill/SKILL.md +252 -116
  155. package/.cursor/skills/api-development/SKILL.md +83 -377
  156. package/.cursor/skills/architecture-design/SKILL.md +138 -632
  157. package/.cursor/skills/backend-annotations/SKILL.md +134 -506
  158. package/.cursor/skills/banana-image/SKILL.md +10 -3
  159. package/.cursor/skills/brainstorm/SKILL.md +103 -535
  160. package/.cursor/skills/bug-detective/SKILL.md +147 -1097
  161. package/.cursor/skills/bug-detective/references/error-patterns.md +242 -0
  162. package/.cursor/skills/code-patterns/SKILL.md +116 -426
  163. package/.cursor/skills/code-patterns/references/leniu-code-patterns.md +87 -0
  164. package/.cursor/skills/crud-development/SKILL.md +64 -304
  165. package/.cursor/skills/data-permission/SKILL.md +105 -412
  166. package/.cursor/skills/data-permission/references/custom-data-scope.md +90 -0
  167. package/.cursor/skills/file-oss-management/SKILL.md +106 -714
  168. package/.cursor/skills/file-oss-management/references/entities.md +105 -0
  169. package/.cursor/skills/file-oss-management/references/service-impl.md +104 -0
  170. package/.cursor/skills/git-workflow/SKILL.md +27 -5
  171. package/.cursor/skills/leniu-api-development/SKILL.md +142 -626
  172. package/.cursor/skills/leniu-api-development/references/real-examples.md +273 -0
  173. package/.cursor/skills/leniu-architecture-design/SKILL.md +176 -391
  174. package/.cursor/skills/leniu-backend-annotations/SKILL.md +132 -519
  175. package/.cursor/skills/leniu-brainstorm/SKILL.md +132 -541
  176. package/.cursor/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
  177. package/.cursor/skills/leniu-crud-development/SKILL.md +232 -938
  178. package/.cursor/skills/leniu-crud-development/references/templates.md +597 -0
  179. package/.cursor/skills/leniu-customization-location/SKILL.md +410 -0
  180. package/.cursor/skills/leniu-data-permission/SKILL.md +70 -0
  181. package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
  182. package/.cursor/skills/leniu-java-entity/SKILL.md +76 -590
  183. package/.cursor/skills/leniu-java-entity/references/templates.md +237 -0
  184. package/.cursor/skills/leniu-java-export/SKILL.md +94 -379
  185. package/.cursor/skills/leniu-java-logging/SKILL.md +106 -709
  186. package/.cursor/skills/leniu-java-logging/references/data-mask.md +46 -0
  187. package/.cursor/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
  188. package/.cursor/skills/leniu-java-mybatis/SKILL.md +73 -446
  189. package/.cursor/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
  190. package/.cursor/skills/leniu-report-customization/SKILL.md +111 -325
  191. package/.cursor/skills/leniu-report-customization/references/table-fields.md +93 -0
  192. package/.cursor/skills/leniu-report-standard-customization/SKILL.md +328 -0
  193. package/.cursor/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
  194. package/.cursor/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
  195. package/.cursor/skills/leniu-security-guard/SKILL.md +133 -347
  196. package/.cursor/skills/mysql-debug/SKILL.md +364 -0
  197. package/.cursor/skills/openspec-apply-change/SKILL.md +10 -1
  198. package/.cursor/skills/openspec-archive-change/SKILL.md +9 -1
  199. package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +9 -1
  200. package/.cursor/skills/openspec-continue-change/SKILL.md +9 -1
  201. package/.cursor/skills/openspec-explore/SKILL.md +10 -1
  202. package/.cursor/skills/openspec-ff-change/SKILL.md +9 -1
  203. package/.cursor/skills/openspec-new-change/SKILL.md +9 -1
  204. package/.cursor/skills/openspec-onboard/SKILL.md +15 -130
  205. package/.cursor/skills/openspec-sync-specs/SKILL.md +9 -1
  206. package/.cursor/skills/openspec-verify-change/SKILL.md +9 -1
  207. package/.cursor/skills/performance-doctor/SKILL.md +110 -434
  208. package/.cursor/skills/redis-cache/SKILL.md +89 -595
  209. package/.cursor/skills/redis-cache/references/listeners.md +23 -0
  210. package/.cursor/skills/scheduled-jobs/SKILL.md +88 -407
  211. package/.cursor/skills/security-guard/SKILL.md +137 -532
  212. package/.cursor/skills/security-guard/references/encrypt-config.md +103 -0
  213. package/.cursor/skills/security-guard/references/sensitive-strategies.md +42 -0
  214. package/.cursor/skills/sms-mail/SKILL.md +116 -574
  215. package/.cursor/skills/sms-mail/references/mail-config.md +88 -0
  216. package/.cursor/skills/sms-mail/references/sms-config.md +74 -0
  217. package/.cursor/skills/social-login/SKILL.md +112 -514
  218. package/.cursor/skills/social-login/references/provider-configs.md +118 -0
  219. package/.cursor/skills/tenant-management/SKILL.md +129 -444
  220. package/.cursor/skills/tenant-management/references/tenant-scenarios.md +91 -0
  221. package/.cursor/skills/test-development/SKILL.md +86 -540
  222. package/.cursor/skills/test-development/references/parameterized-examples.md +119 -0
  223. package/.cursor/skills/utils-toolkit/SKILL.md +52 -305
  224. package/.cursor/skills/utils-toolkit/references/redis-utils-api.md +56 -0
  225. package/.cursor/skills/websocket-sse/SKILL.md +105 -550
  226. package/.cursor/skills/workflow-engine/SKILL.md +147 -502
  227. package/AGENTS.md +1 -0
  228. package/package.json +1 -1
@@ -25,91 +25,45 @@ description: |
25
25
 
26
26
  ## 1. Sa-Token 认证授权
27
27
 
28
- ### 1.1 后端权限注解
28
+ ### 1.1 权限注解
29
29
 
30
30
  ```java
31
31
  import cn.dev33.satoken.annotation.*;
32
- import cn.dev33.satoken.stp.StpUtil;
33
32
 
34
- // 登录校验
35
- @SaCheckLogin
36
- @GetMapping("/userInfo")
37
- public R<UserVo> getUserInfo() { }
33
+ @SaCheckLogin // 登录校验
34
+ @SaCheckPermission("system:user:add") // 权限校验
35
+ @SaCheckRole("admin") // 角色校验
36
+ @SaCheckSafe // 二级认证(敏感操作)
38
37
 
39
- // 权限校验
40
- @SaCheckPermission("system:user:add")
41
- @PostMapping("/addUser")
42
- public R<Long> addUser(@RequestBody UserBo bo) { }
43
-
44
- // 角色校验
45
- @SaCheckRole("admin")
46
- @DeleteMapping("/deleteUser/{id}")
47
- public R<Void> deleteUser(@PathVariable Long id) { }
48
-
49
- // 多权限校验(满足其一)
38
+ // 多权限(满足其一 / 全部满足)
50
39
  @SaCheckPermission(value = {"system:user:add", "system:user:update"}, mode = SaMode.OR)
51
-
52
- // 多权限校验(全部满足)
53
40
  @SaCheckPermission(value = {"system:user:add", "system:user:update"}, mode = SaMode.AND)
54
41
 
55
- // 多角色校验(满足其一)
42
+ // 多角色(满足其一)
56
43
  @SaCheckRole(value = {"admin", "editor"}, mode = SaMode.OR)
57
-
58
- // 二级认证(敏感操作需要再次验证)
59
- @SaCheckSafe
60
44
  ```
61
45
 
62
- ### 1.2 LoginHelper 工具类(核心)
46
+ ### 1.2 LoginHelper 工具类
63
47
 
64
- > **位置**:`ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java`
48
+ > 位置:`ruoyi-common-satoken/.../utils/LoginHelper.java`
65
49
 
66
50
  ```java
67
51
  import org.dromara.common.satoken.utils.LoginHelper;
68
52
 
69
- // ==================== 用户信息获取 ====================
70
-
71
- // 获取当前登录用户完整信息
53
+ // 用户信息
72
54
  LoginUser user = LoginHelper.getLoginUser();
73
-
74
- // 获取用户ID
75
- Long userId = LoginHelper.getUserId();
76
- String userIdStr = LoginHelper.getUserIdStr();
77
-
78
- // 获取用户名
79
- String userName = LoginHelper.getUsername();
80
-
81
- // 获取租户ID(多租户场景)
82
- String tenantId = LoginHelper.getTenantId();
83
-
84
- // 获取部门信息
85
- Long deptId = LoginHelper.getDeptId();
86
- String deptName = LoginHelper.getDeptName();
87
- String deptCategory = LoginHelper.getDeptCategory();
88
-
89
- // 根据 Token 获取用户信息
90
- LoginUser user = LoginHelper.getLoginUser(token);
91
-
92
- // ==================== 管理员判断 ====================
93
-
94
- // 判断是否为超级管理员(userId = 1)
95
- boolean isSuperAdmin = LoginHelper.isSuperAdmin();
96
- boolean isSuperAdmin = LoginHelper.isSuperAdmin(userId);
97
-
98
- // 判断是否为租户管理员
99
- boolean isTenantAdmin = LoginHelper.isTenantAdmin();
100
- boolean isTenantAdmin = LoginHelper.isTenantAdmin(rolePermissions);
101
-
102
- // 检查是否已登录
103
- boolean isLogin = LoginHelper.isLogin();
104
-
105
- // ==================== 用户类型 ====================
106
-
107
- // 获取用户类型(PC端用户、APP用户等)
108
- UserType userType = LoginHelper.getUserType();
109
-
110
- // ==================== 用户登录 ====================
111
-
112
- // 用户登录(支持多设备类型)
55
+ Long userId = LoginHelper.getUserId();
56
+ String name = LoginHelper.getUsername();
57
+ String tenant = LoginHelper.getTenantId();
58
+ Long deptId = LoginHelper.getDeptId();
59
+
60
+ // 管理员判断
61
+ LoginHelper.isSuperAdmin(); // userId = 1
62
+ LoginHelper.isTenantAdmin(); // 租户管理员
63
+ LoginHelper.isLogin(); // 是否已登录
64
+
65
+ // 用户类型 & 登录
66
+ UserType type = LoginHelper.getUserType();
113
67
  LoginHelper.login(loginUser, loginParameter);
114
68
  ```
115
69
 
@@ -119,309 +73,144 @@ LoginHelper.login(loginUser, loginParameter);
119
73
  |------|-----|------|
120
74
  | 超级管理员角色 | `superadmin` | 拥有所有权限 |
121
75
  | 租户管理员角色 | `admin` | 租户内所有权限 |
122
- | 通配符权限 | `*:*:*` | 拥有所有权限标识 |
76
+ | 通配符权限 | `*:*:*` | 所有权限标识 |
123
77
  | 超级管理员ID | `1L` | 系统超管用户ID |
124
78
 
125
79
  ---
126
80
 
127
81
  ## 2. 数据脱敏(@Sensitive)
128
82
 
129
- > **位置**:`ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/`
130
-
131
- ### 2.1 脱敏策略一览(17种)
132
-
133
- | 策略 | 枚举值 | 脱敏效果 | 说明 |
134
- |------|--------|---------|------|
135
- | 身份证 | `ID_CARD` | `110***********1234` | 保留前3位和后4位 |
136
- | 手机号 | `PHONE` | `138****8888` | 保留前3位和后4位 |
137
- | 邮箱 | `EMAIL` | `t**@example.com` | 保留用户名首尾和完整域名 |
138
- | 银行卡 | `BANK_CARD` | `6222***********1234` | 保留前4位和后4位 |
139
- | 中文姓名 | `CHINESE_NAME` | `张*` | 保留姓氏,名字用*代替 |
140
- | 地址 | `ADDRESS` | `北京市朝阳区****` | 保留前8个字符 |
141
- | 固定电话 | `FIXED_PHONE` | `010****1234` | 保留区号和后4位 |
142
- | 密码 | `PASSWORD` | `******` | 全部用*代替 |
143
- | IPv4 地址 | `IPV4` | `192.168.***.***` | 保留网络段,隐藏主机段 |
144
- | IPv6 地址 | `IPV6` | 部分隐藏 | 保留前缀,隐藏接口标识 |
145
- | 车牌号 | `CAR_LICENSE` | `京A***12` | 支持普通和新能源车辆 |
146
- | 用户ID | `USER_ID` | 随机数字 | 生成随机数字替代 |
147
- | 首字符保留 | `FIRST_MASK` | `张***` | 只显示第一个字符 |
148
- | 通用掩码 | `STRING_MASK` | `1234**7890` | 前4位+4个*+后4位 |
149
- | 高安全级别 | `MASK_HIGH_SECURITY` | `to******en` | Token/私钥脱敏,前2后2可见 |
150
- | 清空 | `CLEAR` | 空字符串 | 返回空字符串 |
151
- | 置空 | `CLEAR_TO_NULL` | `null` | 返回 null |
152
-
153
- ### 2.2 基本用法
83
+ > 位置:`ruoyi-common-sensitive/.../`
84
+ > 完整 17 种策略详见 `references/sensitive-strategies.md`
85
+
86
+ ### 基本用法
154
87
 
155
88
  ```java
156
89
  import org.dromara.common.sensitive.annotation.Sensitive;
157
90
  import org.dromara.common.sensitive.core.SensitiveStrategy;
158
91
 
159
92
  public class UserVo {
93
+ @Sensitive(strategy = SensitiveStrategy.PHONE) // 138****8888
94
+ private String phone;
160
95
 
161
- // 手机号脱敏
162
- @Sensitive(strategy = SensitiveStrategy.PHONE)
163
- private String phone; // 138****8888
164
-
165
- // 身份证脱敏
166
- @Sensitive(strategy = SensitiveStrategy.ID_CARD)
167
- private String idCard; // 110***********1234
168
-
169
- // 邮箱脱敏
170
- @Sensitive(strategy = SensitiveStrategy.EMAIL)
171
- private String email; // t**@example.com
96
+ @Sensitive(strategy = SensitiveStrategy.ID_CARD) // 110***********1234
97
+ private String idCard;
172
98
 
173
- // 银行卡脱敏
174
- @Sensitive(strategy = SensitiveStrategy.BANK_CARD)
175
- private String bankCard; // 6222***********1234
99
+ @Sensitive(strategy = SensitiveStrategy.EMAIL) // t**@example.com
100
+ private String email;
176
101
 
177
- // 中文姓名脱敏
178
- @Sensitive(strategy = SensitiveStrategy.CHINESE_NAME)
179
- private String realName; // 张*
102
+ @Sensitive(strategy = SensitiveStrategy.BANK_CARD) // 6222***********1234
103
+ private String bankCard;
180
104
 
181
- // 地址脱敏
182
- @Sensitive(strategy = SensitiveStrategy.ADDRESS)
183
- private String address; // 北京市朝阳区****
105
+ @Sensitive(strategy = SensitiveStrategy.CHINESE_NAME) // 张*
106
+ private String realName;
184
107
 
185
- // 密码脱敏(显示为******)
186
- @Sensitive(strategy = SensitiveStrategy.PASSWORD)
108
+ @Sensitive(strategy = SensitiveStrategy.PASSWORD) // ******
187
109
  private String password;
188
-
189
- // IP 地址脱敏
190
- @Sensitive(strategy = SensitiveStrategy.IPV4)
191
- private String loginIp; // 192.168.***.***
192
-
193
- // 车牌号脱敏
194
- @Sensitive(strategy = SensitiveStrategy.CAR_LICENSE)
195
- private String carNumber; // 京A***12
196
110
  }
197
111
  ```
198
112
 
199
- ### 2.3 基于角色/权限的脱敏控制
113
+ ### 基于角色/权限的脱敏控制
200
114
 
201
115
  ```java
202
- public class UserVo {
203
-
204
- // admin 角色可查看原数据,其他用户看脱敏数据
205
- @Sensitive(strategy = SensitiveStrategy.ID_CARD, roleKey = {"admin"})
206
- private String idCard;
207
-
208
- // 需要 system:user:detail 权限才能看原数据
209
- @Sensitive(strategy = SensitiveStrategy.PHONE, perms = {"system:user:detail"})
210
- private String phone;
211
-
212
- // admin 角色 拥有 finance:account:query 权限都可查看原数据
213
- // roleKey 和 perms 是 OR 关系
214
- @Sensitive(strategy = SensitiveStrategy.BANK_CARD,
215
- roleKey = {"admin"},
216
- perms = {"finance:account:query"})
217
- private String bankCard;
218
-
219
- // 多角色(任一角色即可)
220
- @Sensitive(strategy = SensitiveStrategy.EMAIL,
221
- roleKey = {"admin", "finance"})
222
- private String email;
223
-
224
- // 多权限(任一权限即可)
225
- @Sensitive(strategy = SensitiveStrategy.ADDRESS,
226
- perms = {"system:user:detail", "system:user:update"})
227
- private String address;
228
- }
116
+ // admin 角色可查看原数据,其他用户看脱敏数据
117
+ @Sensitive(strategy = SensitiveStrategy.ID_CARD, roleKey = {"admin"})
118
+ private String idCard;
119
+
120
+ // 需要权限才能看原数据
121
+ @Sensitive(strategy = SensitiveStrategy.PHONE, perms = {"system:user:detail"})
122
+ private String phone;
123
+
124
+ // roleKey 和 perms 是 OR 关系
125
+ @Sensitive(strategy = SensitiveStrategy.BANK_CARD,
126
+ roleKey = {"admin"}, perms = {"finance:account:query"})
127
+ private String bankCard;
229
128
  ```
230
129
 
231
- ### 2.4 日志脱敏
130
+ ### 日志脱敏
232
131
 
233
132
  ```java
234
- import cn.hutool.core.util.DesensitizedUtil;
235
-
236
- // 不好:记录敏感信息
237
- log.info("用户登录,手机号: {}", phone);
238
-
239
- // ✅ 好的:脱敏后记录
240
- log.info("用户登录,手机号: {}", DesensitizedUtil.mobilePhone(phone));
241
- log.info("用户登录,身份证: {}", DesensitizedUtil.idCardNum(idCard, 3, 4));
133
+ // NG: log.info("手机号: {}", phone);
134
+ // OK:
135
+ log.info("手机号: {}", DesensitizedUtil.mobilePhone(phone));
242
136
  ```
243
137
 
244
138
  ---
245
139
 
246
140
  ## 3. 数据加密(@EncryptField / @ApiEncrypt)
247
141
 
248
- > **位置**:`ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/`
142
+ > 位置:`ruoyi-common-encrypt/.../`
143
+ > 完整加密配置和工具类详见 `references/encrypt-config.md`
249
144
 
250
- ### 3.1 支持的加密算法(5种)
145
+ ### 支持算法
251
146
 
252
- | 算法 | 枚举值 | 类型 | 密钥要求 | 说明 |
253
- |------|--------|------|---------|------|
254
- | BASE64 | `BASE64` | 编码 | 无 | 仅编码,不是加密 |
255
- | AES | `AES` | 对称加密 | 16/24/32 位 | 标准对称加密 |
256
- | RSA | `RSA` | 非对称加密 | 公钥/私钥 | 标准非对称加密 |
257
- | SM2 | `SM2` | 国密非对称 | 公钥/私钥 | 国密算法 |
258
- | SM4 | `SM4` | 国密对称 | 16 位 | 国密算法 |
147
+ | 算法 | 类型 | 密钥要求 |
148
+ |------|------|---------|
149
+ | BASE64 | 编码 | 无 |
150
+ | AES | 对称加密 | 16/24/32 位 |
151
+ | RSA | 非对称加密 | 公钥/私钥 |
152
+ | SM2 | 国密非对称 | 公钥/私钥 |
153
+ | SM4 | 国密对称 | 16 位 |
259
154
 
260
- ### 3.2 字段级加密 @EncryptField
155
+ ### 字段级加密
261
156
 
262
157
  ```java
263
158
  import org.dromara.common.encrypt.annotation.EncryptField;
264
159
  import org.dromara.common.encrypt.enumd.AlgorithmType;
265
- import org.dromara.common.encrypt.enumd.EncodeType;
266
160
 
267
161
  public class User {
268
-
269
- // 默认加密(使用全局配置)
270
- @EncryptField
162
+ @EncryptField // 默认(全局配置)
271
163
  private String password;
272
164
 
273
- // 指定 AES 加密
274
- @EncryptField(algorithm = AlgorithmType.AES)
165
+ @EncryptField(algorithm = AlgorithmType.AES) // AES
275
166
  private String idCard;
276
167
 
277
- // 指定 SM4 国密加密
278
- @EncryptField(algorithm = AlgorithmType.SM4)
168
+ @EncryptField(algorithm = AlgorithmType.SM4) // SM4 国密
279
169
  private String phone;
280
-
281
- // 指定编码方式
282
- @EncryptField(algorithm = AlgorithmType.AES, encode = EncodeType.HEX)
283
- private String bankCard;
284
170
  }
285
171
  ```
286
172
 
287
- ### 3.3 API 级加密 @ApiEncrypt
173
+ ### API 级加密
288
174
 
289
175
  ```java
290
176
  import org.dromara.common.encrypt.annotation.ApiEncrypt;
291
177
 
292
- @RestController
293
- public class UserController {
294
-
295
- // 请求体加密(AES+RSA 混合加密)
296
- @ApiEncrypt
297
- @PostMapping("/addUser")
298
- public R<Long> addUser(@RequestBody UserBo bo) {
299
- // 请求体会自动解密
300
- return R.ok(userService.add(bo));
301
- }
302
-
303
- // 请求解密 + 响应加密
304
- @ApiEncrypt(response = true)
305
- @PostMapping("/updateUser")
306
- public R<Void> updateUser(@RequestBody UserBo bo) {
307
- // 请求体自动解密,响应体自动加密
308
- return R.status(userService.update(bo));
309
- }
310
- }
311
- ```
312
-
313
- ### 3.4 EncryptUtils 工具类
314
-
315
- ```java
316
- import org.dromara.common.encrypt.utils.EncryptUtils;
317
-
318
- // AES 加密/解密
319
- String encrypted = EncryptUtils.encryptByAes(data, aesKey);
320
- String decrypted = EncryptUtils.decryptByAes(encrypted, aesKey);
321
-
322
- // RSA 加密/解密(公钥加密,私钥解密)
323
- String encrypted = EncryptUtils.encryptByRsa(data, publicKey);
324
- String decrypted = EncryptUtils.decryptByRsa(encrypted, privateKey);
325
-
326
- // SM2 国密加密/解密
327
- String encrypted = EncryptUtils.encryptBySm2(data, publicKey);
328
- String decrypted = EncryptUtils.decryptBySm2(encrypted, privateKey);
329
-
330
- // SM4 国密加密/解密
331
- String encrypted = EncryptUtils.encryptBySm4(data, sm4Key);
332
- String decrypted = EncryptUtils.decryptBySm4(encrypted, sm4Key);
333
-
334
- // BASE64 编码/解码
335
- String encoded = EncryptUtils.encryptByBase64(data);
336
- String decoded = EncryptUtils.decryptByBase64(encoded);
337
- ```
178
+ @ApiEncrypt // 请求体自动解密
179
+ @PostMapping("/addUser")
180
+ public R<Long> addUser(@RequestBody UserBo bo) { }
338
181
 
339
- ### 3.5 加密配置
340
-
341
- ```yaml
342
- # application.yml
343
- mybatis-encryptor:
344
- # 是否启用加密
345
- enable: true
346
- # 默认加密算法
347
- algorithm: AES
348
- # AES 密钥(16/24/32位)
349
- password: your-aes-key-16bit
350
- # 编码方式
351
- encode: BASE64
352
- # RSA 公钥
353
- publicKey: xxx
354
- # RSA 私钥
355
- privateKey: xxx
182
+ @ApiEncrypt(response = true) // 请求解密 + 响应加密
183
+ @PostMapping("/updateUser")
184
+ public R<Void> updateUser(@RequestBody UserBo bo) { }
356
185
  ```
357
186
 
358
187
  ---
359
188
 
360
189
  ## 4. 接口限流(@RateLimiter)
361
190
 
362
- > **位置**:`ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/`
363
-
364
- ### 4.1 限流类型
365
-
366
- | 类型 | 枚举值 | 说明 |
367
- |------|--------|------|
368
- | 全局限流 | `DEFAULT` | 所有请求共享配额 |
369
- | IP 限流 | `IP` | 每个 IP 独立计算 |
370
- | 集群限流 | `CLUSTER` | 每个集群节点独立 |
371
-
372
- ### 4.2 使用示例
191
+ > 位置:`ruoyi-common-ratelimiter/.../`
373
192
 
374
193
  ```java
375
194
  import org.dromara.common.ratelimiter.annotation.RateLimiter;
376
195
  import org.dromara.common.ratelimiter.enums.LimitType;
377
196
 
378
- @RestController
379
- public class ApiController {
380
-
381
- // 基本用法:60秒内最多100次
382
- @RateLimiter(time = 60, count = 100)
383
- @GetMapping("/list")
384
- public R<List<XxxVo>> list() { }
385
-
386
- // IP 限流:每个 IP 每分钟最多10次
387
- @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
388
- @PostMapping("/login")
389
- public R<String> login() { }
390
-
391
- // 动态 key:基于用户ID限流
392
- @RateLimiter(key = "#userId", time = 60, count = 5)
393
- @PostMapping("/submit")
394
- public R<Void> submit(Long userId) { }
395
-
396
- // SpEL 表达式
397
- @RateLimiter(key = "#{#user.id + ':' + #action}", time = 60, count = 5)
398
- @PostMapping("/action")
399
- public R<Void> doAction(User user, String action) { }
400
-
401
- // 自定义错误消息
402
- @RateLimiter(time = 60, count = 10, message = "访问过于频繁,请稍后再试")
403
- @GetMapping("/data")
404
- public R<DataVo> getData() { }
405
-
406
- // 集群限流
407
- @RateLimiter(time = 60, count = 1000, limitType = LimitType.CLUSTER)
408
- @GetMapping("/global")
409
- public R<Void> globalApi() { }
410
- }
411
- ```
197
+ // 全局限流:60秒内最多100次
198
+ @RateLimiter(time = 60, count = 100)
412
199
 
413
- ### 4.3 注解参数
200
+ // IP 限流:每个 IP 每分钟最多10次
201
+ @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
414
202
 
415
- | 参数 | 类型 | 默认值 | 说明 |
416
- |------|------|--------|------|
417
- | `key` | String | `""` | 限流 key,支持 SpEL 表达式 |
418
- | `time` | int | `60` | 时间窗口(秒) |
419
- | `count` | int | `100` | 最大请求次数 |
420
- | `limitType` | LimitType | `DEFAULT` | 限流类型 |
421
- | `message` | String | 国际化 key | 错误提示消息 |
422
- | `timeout` | int | `86400` | Redis 超时时间(秒) |
203
+ // 动态 key(SpEL)
204
+ @RateLimiter(key = "#userId", time = 60, count = 5)
423
205
 
424
- ### 4.4 推荐配置
206
+ // 自定义错误消息
207
+ @RateLimiter(time = 60, count = 10, message = "访问过于频繁,请稍后再试")
208
+
209
+ // 集群限流
210
+ @RateLimiter(time = 60, count = 1000, limitType = LimitType.CLUSTER)
211
+ ```
212
+
213
+ ### 推荐配置
425
214
 
426
215
  | 场景 | time | count | limitType |
427
216
  |------|------|-------|-----------|
@@ -435,48 +224,15 @@ public class ApiController {
435
224
 
436
225
  ## 5. 防重复提交(@RepeatSubmit)
437
226
 
438
- > **位置**:`ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/`
439
-
440
- ### 5.1 使用示例
227
+ > 位置:`ruoyi-common-idempotent/.../`
441
228
 
442
229
  ```java
443
230
  import org.dromara.common.idempotent.annotation.RepeatSubmit;
444
- import java.util.concurrent.TimeUnit;
445
-
446
- @RestController
447
- public class OrderController {
448
-
449
- // 默认:5秒内不能重复提交
450
- @RepeatSubmit()
451
- @PostMapping("/addOrder")
452
- public R<Long> addOrder(@RequestBody OrderBo bo) { }
453
-
454
- // 自定义间隔:10秒(毫秒单位)
455
- @RepeatSubmit(interval = 10000)
456
- @PostMapping("/pay")
457
- public R<Void> pay(@RequestBody PayBo bo) { }
458
-
459
- // 使用秒作为单位
460
- @RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS)
461
- @PostMapping("/submit")
462
- public R<Void> submit(@RequestBody SubmitBo bo) { }
463
-
464
- // 自定义提示消息
465
- @RepeatSubmit(interval = 5000, message = "请勿重复提交订单")
466
- @PostMapping("/createOrder")
467
- public R<Long> createOrder(@RequestBody OrderBo bo) { }
468
- }
469
- ```
470
231
 
471
- ### 5.2 注解参数
472
-
473
- | 参数 | 类型 | 默认值 | 说明 |
474
- |------|------|--------|------|
475
- | `interval` | int | `5000` | 间隔时间 |
476
- | `timeUnit` | TimeUnit | `MILLISECONDS` | 时间单位 |
477
- | `message` | String | 国际化 key | 错误提示消息 |
478
-
479
- ### 5.3 推荐配置
232
+ @RepeatSubmit() // 默认 5
233
+ @RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS) // 10 秒
234
+ @RepeatSubmit(interval = 5000, message = "请勿重复提交订单") // 自定义消息
235
+ ```
480
236
 
481
237
  | 场景 | 推荐间隔 |
482
238
  |------|---------|
@@ -489,220 +245,78 @@ public class OrderController {
489
245
 
490
246
  ## 6. 数据权限(@DataPermission)
491
247
 
492
- > **完整指南请参考 `data-permission` 技能**,本节仅作简要说明。
493
-
494
- 数据权限是行级数据过滤机制,通过 MyBatis 拦截器自动注入 WHERE 条件。
495
-
496
- ### 快速示例
248
+ > **完整指南请使用 `data-permission` 技能**
497
249
 
498
250
  ```java
499
251
  @DataPermission({
500
252
  @DataColumn(key = "deptName", value = "create_dept"),
501
253
  @DataColumn(key = "userName", value = "create_by")
502
254
  })
503
- @Override
504
- public TableDataInfo<OrderVo> pageWithPermission(OrderBo bo, PageQuery pageQuery) {
505
- return page(buildQueryWrapper(bo), pageQuery).convert(OrderVo.class);
506
- }
255
+ public TableDataInfo<OrderVo> pageWithPermission(OrderBo bo, PageQuery pageQuery) { }
507
256
  ```
508
257
 
509
- ### 权限类型
510
-
511
- | 类型 | 说明 |
512
- |------|------|
513
- | 全部数据 | 无过滤 |
514
- | 本部门 | 只看本部门 |
515
- | 本部门及以下 | 本部门 + 子部门 |
516
- | 仅本人 | 只看自己创建的 |
517
- | 自定义 | 按角色关联的部门 |
518
-
519
- **更多内容**(表别名配置、临时忽略、扩展自定义类型、问题排查)请使用 `data-permission` 技能。
258
+ 权限类型:全部数据 | 本部门 | 本部门及以下 | 仅本人 | 自定义
520
259
 
521
260
  ---
522
261
 
523
262
  ## 7. 输入校验
524
263
 
525
264
  ```java
526
- import jakarta.validation.constraints.*;
527
265
  import org.dromara.common.core.validate.AddGroup;
528
266
  import org.dromara.common.core.validate.EditGroup;
529
267
 
530
268
  public class UserBo {
531
-
532
269
  @NotNull(message = "ID不能为空", groups = { EditGroup.class })
533
270
  private Long id;
534
271
 
535
272
  @NotBlank(message = "用户名不能为空", groups = { AddGroup.class, EditGroup.class })
536
- @Size(min = 2, max = 20, message = "用户名长度2-20")
537
- @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母数字下划线")
273
+ @Size(min = 2, max = 20)
274
+ @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "只能包含字母数字下划线")
538
275
  private String username;
539
-
540
- @NotBlank(message = "密码不能为空", groups = { AddGroup.class })
541
- @Size(min = 6, max = 20, message = "密码长度6-20")
542
- private String password;
543
-
544
- @Email(message = "邮箱格式不正确")
545
- private String email;
546
-
547
- @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
548
- private String phone;
549
276
  }
550
277
 
551
- // Controller 中使用分组校验
552
- @PostMapping("/addUser")
553
- public R<Long> addUser(@Validated(AddGroup.class) @RequestBody UserBo bo) { }
554
-
555
- @PutMapping("/updateUser")
556
- public R<Void> updateUser(@Validated(EditGroup.class) @RequestBody UserBo bo) { }
278
+ // Controller 分组校验
279
+ @PostMapping public R<Long> add(@Validated(AddGroup.class) @RequestBody UserBo bo) { }
280
+ @PutMapping public R<Void> update(@Validated(EditGroup.class) @RequestBody UserBo bo) { }
557
281
  ```
558
282
 
559
283
  ---
560
284
 
561
285
  ## 8. 常见漏洞防护
562
286
 
563
- ### 8.1 SQL 注入防护
287
+ ### SQL 注入
564
288
 
565
289
  ```java
566
- // 危险:字符串拼接
567
- String sql = "SELECT * FROM user WHERE name = '" + name + "'";
568
-
569
- // 安全:使用参数化查询(MyBatis-Plus)
570
- PlusLambdaQuery<User> query = PlusLambdaQuery.of();
571
- query.eq(User::getName, name);
572
-
573
- // ✅ 安全:MyBatis 参数绑定
574
- @Select("SELECT * FROM user WHERE name = #{name}")
575
- User getByName(@Param("name") String name);
576
-
577
- // ❌ 危险:MyBatis 使用 ${}
578
- @Select("SELECT * FROM user WHERE name = '${name}'") // 禁止!
579
-
580
- // ✅ 安全:MyBatis 使用 #{}
581
- @Select("SELECT * FROM user WHERE name = #{name}")
582
- ```
583
-
584
- ### 8.2 XSS 攻击防护
585
-
586
- ```java
587
- import cn.hutool.http.HtmlUtil;
588
-
589
- // 输出时转义 HTML 标签
590
- String safe = HtmlUtil.escape(userInput);
591
-
592
- // 过滤 HTML 标签(移除所有标签)
593
- String text = HtmlUtil.cleanHtmlTag(userInput);
594
-
595
- // 移除危险标签(保留安全标签)
596
- String filtered = HtmlUtil.removeHtmlTag(userInput, "script", "iframe", "object");
290
+ // NG: "SELECT * FROM user WHERE name = '" + name + "'"
291
+ // NG: @Select("SELECT * FROM user WHERE name = '${name}'")
292
+ // OK: MyBatis-Plus LambdaQueryWrapper
293
+ // OK: @Select("SELECT * FROM user WHERE name = #{name}")
597
294
  ```
598
295
 
599
- ### 8.3 越权访问防护
296
+ ### 越权访问
600
297
 
601
298
  ```java
602
- // ✅ 必须校验数据归属(三层架构:Service 直接调用 Mapper)
603
299
  @Override
604
300
  public OrderVo selectById(Long id) {
605
301
  Order order = baseMapper.selectById(id);
606
302
  if (ObjectUtil.isNull(order)) {
607
303
  throw new ServiceException("订单不存在");
608
304
  }
609
-
610
- // 超管可以查看所有数据
611
- if (LoginHelper.isSuperAdmin()) {
612
- return MapstructUtils.convert(order, OrderVo.class);
613
- }
614
-
615
- // 校验数据归属
616
- if (!order.getUserId().equals(LoginHelper.getUserId())) {
305
+ if (!LoginHelper.isSuperAdmin() && !order.getUserId().equals(LoginHelper.getUserId())) {
617
306
  throw new ServiceException("无权访问此订单");
618
307
  }
619
-
620
308
  return MapstructUtils.convert(order, OrderVo.class);
621
309
  }
622
310
 
623
- // ✅ 批量操作也要校验
624
- @Override
625
- @Transactional(rollbackFor = Exception.class)
626
- public int deleteByIds(Collection<Long> ids) {
627
- // 查询要删除的数据
628
- List<Order> orders = baseMapper.selectByIds(ids);
629
-
630
- // 非超管需要校验归属
631
- if (!LoginHelper.isSuperAdmin()) {
632
- Long currentUserId = LoginHelper.getUserId();
633
- boolean hasUnauthorized = orders.stream()
634
- .anyMatch(o -> !o.getUserId().equals(currentUserId));
635
- if (hasUnauthorized) {
636
- throw new ServiceException("包含无权删除的数据");
637
- }
638
- }
639
-
640
- return baseMapper.deleteByIds(ids);
641
- }
311
+ // 批量操作同样校验归属
642
312
  ```
643
313
 
644
- ### 8.4 文件上传安全
314
+ ### 敏感信息泄露
645
315
 
646
316
  ```java
647
- import org.dromara.common.core.exception.ServiceException;
648
-
649
- // 安全的文件上传
650
- private static final Set<String> ALLOWED_TYPES = Set.of(
651
- "image/jpeg", "image/png", "image/gif", "image/webp"
652
- );
653
-
654
- private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
655
-
656
- public void uploadFile(MultipartFile file) {
657
- // 1. 校验文件类型
658
- String contentType = file.getContentType();
659
- if (!ALLOWED_TYPES.contains(contentType)) {
660
- throw new ServiceException("不支持的文件类型");
661
- }
662
-
663
- // 2. 校验文件大小
664
- if (file.getSize() > MAX_FILE_SIZE) {
665
- throw new ServiceException("文件大小不能超过10MB");
666
- }
667
-
668
- // 3. 校验文件扩展名
669
- String originalName = file.getOriginalFilename();
670
- String extension = getExtension(originalName);
671
- if (!Set.of(".jpg", ".jpeg", ".png", ".gif", ".webp").contains(extension.toLowerCase())) {
672
- throw new ServiceException("不支持的文件扩展名");
673
- }
674
-
675
- // 4. 重命名文件,避免路径穿越
676
- String newName = UUID.randomUUID() + extension;
677
-
678
- // 5. 保存文件...
679
- }
680
- ```
681
-
682
- ### 8.5 敏感信息泄露防护
683
-
684
- ```java
685
- // ❌ 危险:返回敏感信息
686
- @GetMapping("/user/{id}")
687
- public User getUser(@PathVariable Long id) {
688
- return userDao.getById(id); // 包含密码等敏感字段
689
- }
690
-
691
- // ✅ 安全:使用 VO 过滤敏感字段
692
- @GetMapping("/getUser/{id}")
693
- public R<UserVo> getUser(@PathVariable Long id) {
694
- User user = userDao.getById(id);
695
- return R.ok(MapstructUtils.convert(user, UserVo.class)); // VO 不包含密码字段
696
- }
697
-
698
- // ✅ 使用 @Sensitive 注解自动脱敏
699
- public class UserVo {
700
- private Long id;
701
- private String userName;
702
-
703
- @Sensitive(strategy = SensitiveStrategy.PHONE)
704
- private String phone; // 自动脱敏为 138****8888
705
- }
317
+ // NG: return userDao.getById(id); // 包含密码等
318
+ // OK: return MapstructUtils.convert(user, UserVo.class); // VO 过滤敏感字段
319
+ // OK: 使用 @Sensitive 自动脱敏
706
320
  ```
707
321
 
708
322
  ---
@@ -711,38 +325,29 @@ public class UserVo {
711
325
 
712
326
  ### 代码审查
713
327
 
714
- - [ ] 所有用户输入都经过 `@NotBlank`/`@Size`/`@Pattern` 等校验?
715
- - [ ] SQL 查询使用 MyBatis-Plus 或参数化查询(#{})?
716
- - [ ] 敏感字段使用 `@Sensitive` 脱敏?
717
- - [ ] 需要加密的字段使用 `@EncryptField`?
718
- - [ ] Controller 方法添加了 `@SaCheckPermission` 注解?
719
- - [ ] 敏感操作添加了 `@RepeatSubmit` 防重复?
720
- - [ ] 高频接口添加了 `@RateLimiter` 限流?
721
- - [ ] 批量操作校验了数据归属(防越权)?
722
- - [ ] 文件上传校验了类型、大小、扩展名?
723
- - [ ] 日志中无敏感信息(或已脱敏)?
724
-
725
- ### 配置检查
726
-
727
- - [ ] 生产环境关闭调试模式?
728
- - [ ] 数据库密码等敏感配置已加密(或使用环境变量)?
729
- - [ ] Token 有效期合理(建议 2-24 小时)?
730
- - [ ] CORS 配置正确(不使用 `*`)?
731
- - [ ] 限流配置合理?
732
-
733
- ### 部署检查
734
-
735
- - [ ] 启用 HTTPS?
736
- - [ ] 配置安全响应头(X-Frame-Options、X-XSS-Protection 等)?
737
- - [ ] 错误页面不泄露堆栈信息?
738
- - [ ] 数据库端口不对外暴露?
739
- - [ ] Redis 设置了密码?
328
+ - [ ] 用户输入经过 `@NotBlank`/`@Size`/`@Pattern` 校验
329
+ - [ ] SQL 使用 MyBatis-Plus 或参数化查询(#{}
330
+ - [ ] 敏感字段使用 `@Sensitive` 脱敏
331
+ - [ ] 需加密字段使用 `@EncryptField`
332
+ - [ ] Controller 添加 `@SaCheckPermission`
333
+ - [ ] 敏感操作添加 `@RepeatSubmit`
334
+ - [ ] 高频接口添加 `@RateLimiter`
335
+ - [ ] 批量操作校验数据归属(防越权)
336
+ - [ ] 文件上传校验类型/大小/扩展名
337
+ - [ ] 日志中无敏感信息(或已脱敏)
338
+
339
+ ### 配置 & 部署
340
+
341
+ - [ ] 生产关闭调试模式
342
+ - [ ] 敏感配置已加密或使用环境变量
343
+ - [ ] Token 有效期合理(2-24h)
344
+ - [ ] CORS 不使用 `*`
345
+ - [ ] 启用 HTTPS、安全响应头
346
+ - [ ] 错误页不泄露堆栈、数据库/Redis 端口不对外
740
347
 
741
348
  ---
742
349
 
743
-
744
-
745
350
  ## 注意事项
746
351
 
747
- - 如果需要 leniu-tengyun-core 项目的安全开发规范,请使用 `leniu-security-guard` skill
748
- - leniu-tengyun-core 使用的是自研 secure 模块,注解和工具类与 RuoYi-Vue-Plus 不同
352
+ - leniu-tengyun-core 项目请使用 `leniu-security-guard` skill
353
+ - leniu 使用自研 secure 模块,注解和工具类与 RuoYi-Vue-Plus 不同