ai-engineering-init 1.1.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 (313) hide show
  1. package/.claude/agents/code-reviewer.md +139 -0
  2. package/.claude/agents/project-manager.md +159 -0
  3. package/.claude/audio/completed.wav +0 -0
  4. package/.claude/commands/add-todo.md +255 -0
  5. package/.claude/commands/check.md +210 -0
  6. package/.claude/commands/crud.md +454 -0
  7. package/.claude/commands/dev.md +503 -0
  8. package/.claude/commands/init-docs.md +681 -0
  9. package/.claude/commands/next.md +251 -0
  10. package/.claude/commands/progress.md +242 -0
  11. package/.claude/commands/start.md +199 -0
  12. package/.claude/commands/sync.md +307 -0
  13. package/.claude/commands/update-status.md +428 -0
  14. package/.claude/docs/Mixin/344/275/277/347/224/250/346/214/207/345/215/227.md +299 -0
  15. package/.claude/docs/README.md +167 -0
  16. package/.claude/docs//345/211/215/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +599 -0
  17. package/.claude/docs//345/220/216/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +726 -0
  18. package/.claude/docs//345/267/245/344/275/234/346/265/201/345/274/200/345/217/221/346/214/207/345/215/227.md +714 -0
  19. package/.claude/docs//345/267/245/345/205/267/347/261/273/344/275/277/347/224/250/346/214/207/345/215/227.md +463 -0
  20. package/.claude/docs//346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/350/247/204/350/214/203.md +390 -0
  21. package/.claude/docs//346/226/260/345/212/237/350/203/275/345/274/200/345/217/221/346/265/201/347/250/213/350/247/204/350/214/203.md +688 -0
  22. package/.claude/docs//346/226/260/351/241/271/347/233/256/345/274/200/345/217/221/346/265/201/347/250/213.md +365 -0
  23. package/.claude/docs//346/241/206/346/236/266/350/257/264/346/230/216.md +393 -0
  24. package/.claude/docs//350/267/257/347/224/261/351/205/215/347/275/256/346/214/207/345/215/227.md +246 -0
  25. package/.claude/framework-config.json +73 -0
  26. package/.claude/hooks/pre-tool-use.js +117 -0
  27. package/.claude/hooks/skill-forced-eval.js +167 -0
  28. package/.claude/hooks/stop.js +58 -0
  29. package/.claude/settings.json +41 -0
  30. package/.claude/skills/add-skill/SKILL.md +352 -0
  31. package/.claude/skills/api-development/SKILL.md +560 -0
  32. package/.claude/skills/architecture-design/SKILL.md +756 -0
  33. package/.claude/skills/backend-annotations/SKILL.md +674 -0
  34. package/.claude/skills/banana-image/CHANGELOG.md +37 -0
  35. package/.claude/skills/banana-image/README.md +146 -0
  36. package/.claude/skills/banana-image/SKILL.md +164 -0
  37. package/.claude/skills/banana-image/assets/logo.png +0 -0
  38. package/.claude/skills/banana-image/references/advanced-usage.md +189 -0
  39. package/.claude/skills/banana-image/scripts/apply_template.py +125 -0
  40. package/.claude/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  41. package/.claude/skills/banana-image/scripts/batch_prep.py +82 -0
  42. package/.claude/skills/banana-image/scripts/package-lock.json +1437 -0
  43. package/.claude/skills/banana-image/scripts/package.json +18 -0
  44. package/.claude/skills/banana-image/scripts/requirements.txt +10 -0
  45. package/.claude/skills/banana-image/templates/poster.json +22 -0
  46. package/.claude/skills/banana-image/templates/product.json +17 -0
  47. package/.claude/skills/banana-image/templates/social.json +22 -0
  48. package/.claude/skills/banana-image/templates/thumbnail.json +17 -0
  49. package/.claude/skills/brainstorm/SKILL.md +648 -0
  50. package/.claude/skills/bug-detective/SKILL.md +1206 -0
  51. package/.claude/skills/code-patterns/SKILL.md +590 -0
  52. package/.claude/skills/collaborating-with-codex/SKILL.md +174 -0
  53. package/.claude/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  54. package/.claude/skills/collaborating-with-gemini/SKILL.md +194 -0
  55. package/.claude/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  56. package/.claude/skills/crud-development/SKILL.md +649 -0
  57. package/.claude/skills/data-permission/SKILL.md +599 -0
  58. package/.claude/skills/database-ops/SKILL.md +407 -0
  59. package/.claude/skills/error-handler/SKILL.md +371 -0
  60. package/.claude/skills/file-oss-management/SKILL.md +863 -0
  61. package/.claude/skills/git-workflow/SKILL.md +375 -0
  62. package/.claude/skills/json-serialization/SKILL.md +357 -0
  63. package/.claude/skills/leniu-api-development/SKILL.md +803 -0
  64. package/.claude/skills/leniu-architecture-design/SKILL.md +598 -0
  65. package/.claude/skills/leniu-backend-annotations/SKILL.md +664 -0
  66. package/.claude/skills/leniu-code-patterns/SKILL.md +365 -0
  67. package/.claude/skills/leniu-crud-development/SKILL.md +1110 -0
  68. package/.claude/skills/leniu-data-permission/SKILL.md +256 -0
  69. package/.claude/skills/leniu-database-ops/SKILL.md +426 -0
  70. package/.claude/skills/leniu-error-handler/SKILL.md +462 -0
  71. package/.claude/skills/leniu-java-amount-handling/SKILL.md +461 -0
  72. package/.claude/skills/leniu-java-code-style/SKILL.md +510 -0
  73. package/.claude/skills/leniu-java-concurrent/SKILL.md +400 -0
  74. package/.claude/skills/leniu-java-entity/SKILL.md +751 -0
  75. package/.claude/skills/leniu-java-export/SKILL.md +560 -0
  76. package/.claude/skills/leniu-java-logging/SKILL.md +832 -0
  77. package/.claude/skills/leniu-java-mq/SKILL.md +338 -0
  78. package/.claude/skills/leniu-java-mybatis/SKILL.md +640 -0
  79. package/.claude/skills/leniu-java-report-query-param/SKILL.md +291 -0
  80. package/.claude/skills/leniu-java-task/SKILL.md +367 -0
  81. package/.claude/skills/leniu-java-total-line/SKILL.md +195 -0
  82. package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  83. package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  84. package/.claude/skills/leniu-mealtime/SKILL.md +215 -0
  85. package/.claude/skills/leniu-redis-cache/SKILL.md +316 -0
  86. package/.claude/skills/leniu-security-guard/SKILL.md +520 -0
  87. package/.claude/skills/leniu-utils-toolkit/SKILL.md +380 -0
  88. package/.claude/skills/openspec-apply-change/SKILL.md +156 -0
  89. package/.claude/skills/openspec-archive-change/SKILL.md +114 -0
  90. package/.claude/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  91. package/.claude/skills/openspec-continue-change/SKILL.md +118 -0
  92. package/.claude/skills/openspec-explore/SKILL.md +290 -0
  93. package/.claude/skills/openspec-ff-change/SKILL.md +101 -0
  94. package/.claude/skills/openspec-new-change/SKILL.md +74 -0
  95. package/.claude/skills/openspec-onboard/SKILL.md +529 -0
  96. package/.claude/skills/openspec-sync-specs/SKILL.md +138 -0
  97. package/.claude/skills/openspec-verify-change/SKILL.md +168 -0
  98. package/.claude/skills/performance-doctor/SKILL.md +627 -0
  99. package/.claude/skills/project-navigator/SKILL.md +305 -0
  100. package/.claude/skills/redis-cache/SKILL.md +839 -0
  101. package/.claude/skills/scheduled-jobs/SKILL.md +633 -0
  102. package/.claude/skills/security-guard/SKILL.md +748 -0
  103. package/.claude/skills/sms-mail/SKILL.md +766 -0
  104. package/.claude/skills/social-login/SKILL.md +668 -0
  105. package/.claude/skills/store-pc/SKILL.md +366 -0
  106. package/.claude/skills/task-tracker/SKILL.md +307 -0
  107. package/.claude/skills/tech-decision/SKILL.md +393 -0
  108. package/.claude/skills/tenant-management/SKILL.md +603 -0
  109. package/.claude/skills/test-development/SKILL.md +755 -0
  110. package/.claude/skills/ui-pc/SKILL.md +438 -0
  111. package/.claude/skills/utils-toolkit/SKILL.md +615 -0
  112. package/.claude/skills/websocket-sse/SKILL.md +716 -0
  113. package/.claude/skills/workflow-engine/SKILL.md +676 -0
  114. package/.claude/templates//345/276/205/345/212/236/346/270/205/345/215/225/346/250/241/346/235/277.md +56 -0
  115. package/.claude/templates//351/234/200/346/261/202/346/226/207/346/241/243/346/250/241/346/235/277.md +85 -0
  116. package/.claude/templates//351/241/271/347/233/256/347/212/266/346/200/201/346/250/241/346/235/277.md +43 -0
  117. package/.codex/skills/add-skill/SKILL.md +352 -0
  118. package/.codex/skills/add-todo/SKILL.md +269 -0
  119. package/.codex/skills/api-development/SKILL.md +693 -0
  120. package/.codex/skills/architecture-design/SKILL.md +628 -0
  121. package/.codex/skills/backend-annotations/SKILL.md +664 -0
  122. package/.codex/skills/banana-image/CHANGELOG.md +37 -0
  123. package/.codex/skills/banana-image/README.md +146 -0
  124. package/.codex/skills/banana-image/SKILL.md +164 -0
  125. package/.codex/skills/banana-image/assets/logo.png +0 -0
  126. package/.codex/skills/banana-image/references/advanced-usage.md +189 -0
  127. package/.codex/skills/banana-image/scripts/apply_template.py +125 -0
  128. package/.codex/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  129. package/.codex/skills/banana-image/scripts/batch_prep.py +82 -0
  130. package/.codex/skills/banana-image/scripts/package-lock.json +1437 -0
  131. package/.codex/skills/banana-image/scripts/package.json +18 -0
  132. package/.codex/skills/banana-image/scripts/requirements.txt +10 -0
  133. package/.codex/skills/banana-image/templates/poster.json +22 -0
  134. package/.codex/skills/banana-image/templates/product.json +17 -0
  135. package/.codex/skills/banana-image/templates/social.json +22 -0
  136. package/.codex/skills/banana-image/templates/thumbnail.json +17 -0
  137. package/.codex/skills/brainstorm/SKILL.md +648 -0
  138. package/.codex/skills/bug-detective/SKILL.md +1206 -0
  139. package/.codex/skills/check/SKILL.md +367 -0
  140. package/.codex/skills/code-patterns/SKILL.md +442 -0
  141. package/.codex/skills/collaborating-with-codex/SKILL.md +174 -0
  142. package/.codex/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  143. package/.codex/skills/collaborating-with-gemini/SKILL.md +194 -0
  144. package/.codex/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  145. package/.codex/skills/crud/SKILL.md +265 -0
  146. package/.codex/skills/crud-development/SKILL.md +637 -0
  147. package/.codex/skills/data-permission/SKILL.md +591 -0
  148. package/.codex/skills/database-ops/SKILL.md +553 -0
  149. package/.codex/skills/dev/SKILL.md +187 -0
  150. package/.codex/skills/error-handler/SKILL.md +361 -0
  151. package/.codex/skills/file-oss-management/SKILL.md +863 -0
  152. package/.codex/skills/git-workflow/SKILL.md +375 -0
  153. package/.codex/skills/init-docs/SKILL.md +194 -0
  154. package/.codex/skills/json-serialization/SKILL.md +357 -0
  155. package/.codex/skills/leniu-api-development/SKILL.md +803 -0
  156. package/.codex/skills/leniu-architecture-design/SKILL.md +594 -0
  157. package/.codex/skills/leniu-backend-annotations/SKILL.md +662 -0
  158. package/.codex/skills/leniu-code-patterns/SKILL.md +365 -0
  159. package/.codex/skills/leniu-crud-development/SKILL.md +1110 -0
  160. package/.codex/skills/leniu-data-permission/SKILL.md +256 -0
  161. package/.codex/skills/leniu-database-ops/SKILL.md +426 -0
  162. package/.codex/skills/leniu-error-handler/SKILL.md +462 -0
  163. package/.codex/skills/leniu-java-amount-handling/SKILL.md +461 -0
  164. package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
  165. package/.codex/skills/leniu-java-concurrent/SKILL.md +400 -0
  166. package/.codex/skills/leniu-java-entity/SKILL.md +751 -0
  167. package/.codex/skills/leniu-java-export/SKILL.md +560 -0
  168. package/.codex/skills/leniu-java-logging/SKILL.md +832 -0
  169. package/.codex/skills/leniu-java-mq/SKILL.md +338 -0
  170. package/.codex/skills/leniu-java-mybatis/SKILL.md +640 -0
  171. package/.codex/skills/leniu-java-report-query-param/SKILL.md +291 -0
  172. package/.codex/skills/leniu-java-task/SKILL.md +367 -0
  173. package/.codex/skills/leniu-java-total-line/SKILL.md +195 -0
  174. package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  175. package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  176. package/.codex/skills/leniu-mealtime/SKILL.md +215 -0
  177. package/.codex/skills/leniu-redis-cache/SKILL.md +316 -0
  178. package/.codex/skills/leniu-security-guard/SKILL.md +520 -0
  179. package/.codex/skills/leniu-utils-toolkit/SKILL.md +378 -0
  180. package/.codex/skills/next/SKILL.md +137 -0
  181. package/.codex/skills/openspec-apply-change/SKILL.md +156 -0
  182. package/.codex/skills/openspec-archive-change/SKILL.md +114 -0
  183. package/.codex/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  184. package/.codex/skills/openspec-continue-change/SKILL.md +118 -0
  185. package/.codex/skills/openspec-explore/SKILL.md +290 -0
  186. package/.codex/skills/openspec-ff-change/SKILL.md +101 -0
  187. package/.codex/skills/openspec-new-change/SKILL.md +74 -0
  188. package/.codex/skills/openspec-onboard/SKILL.md +529 -0
  189. package/.codex/skills/openspec-sync-specs/SKILL.md +138 -0
  190. package/.codex/skills/openspec-verify-change/SKILL.md +168 -0
  191. package/.codex/skills/performance-doctor/SKILL.md +627 -0
  192. package/.codex/skills/progress/SKILL.md +193 -0
  193. package/.codex/skills/project-navigator/SKILL.md +286 -0
  194. package/.codex/skills/redis-cache/SKILL.md +829 -0
  195. package/.codex/skills/scheduled-jobs/SKILL.md +633 -0
  196. package/.codex/skills/security-guard/SKILL.md +739 -0
  197. package/.codex/skills/sms-mail/SKILL.md +766 -0
  198. package/.codex/skills/social-login/SKILL.md +668 -0
  199. package/.codex/skills/start/SKILL.md +154 -0
  200. package/.codex/skills/store-pc/SKILL.md +491 -0
  201. package/.codex/skills/sync/SKILL.md +149 -0
  202. package/.codex/skills/task-tracker/SKILL.md +307 -0
  203. package/.codex/skills/tech-decision/SKILL.md +393 -0
  204. package/.codex/skills/tenant-management/SKILL.md +603 -0
  205. package/.codex/skills/test-development/SKILL.md +755 -0
  206. package/.codex/skills/ui-pc/SKILL.md +475 -0
  207. package/.codex/skills/update-status/SKILL.md +159 -0
  208. package/.codex/skills/utils-toolkit/SKILL.md +593 -0
  209. package/.codex/skills/websocket-sse/SKILL.md +716 -0
  210. package/.codex/skills/workflow-engine/SKILL.md +676 -0
  211. package/.cursor/agents/code-reviewer.md +139 -0
  212. package/.cursor/agents/project-manager.md +159 -0
  213. package/.cursor/commands/opsx-apply.md +152 -0
  214. package/.cursor/commands/opsx-archive.md +157 -0
  215. package/.cursor/commands/opsx-bulk-archive.md +242 -0
  216. package/.cursor/commands/opsx-continue.md +114 -0
  217. package/.cursor/commands/opsx-explore.md +174 -0
  218. package/.cursor/commands/opsx-ff.md +94 -0
  219. package/.cursor/commands/opsx-new.md +69 -0
  220. package/.cursor/commands/opsx-onboard.md +525 -0
  221. package/.cursor/commands/opsx-sync.md +134 -0
  222. package/.cursor/commands/opsx-verify.md +164 -0
  223. package/.cursor/mcp.json +22 -0
  224. package/.cursor/skills/add-skill/SKILL.md +352 -0
  225. package/.cursor/skills/api-development/SKILL.md +560 -0
  226. package/.cursor/skills/architecture-design/SKILL.md +756 -0
  227. package/.cursor/skills/backend-annotations/SKILL.md +674 -0
  228. package/.cursor/skills/banana-image/CHANGELOG.md +37 -0
  229. package/.cursor/skills/banana-image/README.md +146 -0
  230. package/.cursor/skills/banana-image/SKILL.md +164 -0
  231. package/.cursor/skills/banana-image/assets/logo.png +0 -0
  232. package/.cursor/skills/banana-image/references/advanced-usage.md +189 -0
  233. package/.cursor/skills/banana-image/scripts/apply_template.py +125 -0
  234. package/.cursor/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  235. package/.cursor/skills/banana-image/scripts/batch_prep.py +82 -0
  236. package/.cursor/skills/banana-image/scripts/package-lock.json +1437 -0
  237. package/.cursor/skills/banana-image/scripts/package.json +18 -0
  238. package/.cursor/skills/banana-image/scripts/requirements.txt +10 -0
  239. package/.cursor/skills/banana-image/templates/poster.json +22 -0
  240. package/.cursor/skills/banana-image/templates/product.json +17 -0
  241. package/.cursor/skills/banana-image/templates/social.json +22 -0
  242. package/.cursor/skills/banana-image/templates/thumbnail.json +17 -0
  243. package/.cursor/skills/brainstorm/SKILL.md +648 -0
  244. package/.cursor/skills/bug-detective/SKILL.md +1206 -0
  245. package/.cursor/skills/code-patterns/SKILL.md +590 -0
  246. package/.cursor/skills/collaborating-with-codex/SKILL.md +174 -0
  247. package/.cursor/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  248. package/.cursor/skills/collaborating-with-gemini/SKILL.md +194 -0
  249. package/.cursor/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  250. package/.cursor/skills/crud-development/SKILL.md +649 -0
  251. package/.cursor/skills/data-permission/SKILL.md +599 -0
  252. package/.cursor/skills/database-ops/SKILL.md +407 -0
  253. package/.cursor/skills/error-handler/SKILL.md +371 -0
  254. package/.cursor/skills/file-oss-management/SKILL.md +863 -0
  255. package/.cursor/skills/git-workflow/SKILL.md +375 -0
  256. package/.cursor/skills/json-serialization/SKILL.md +357 -0
  257. package/.cursor/skills/leniu-api-development/SKILL.md +803 -0
  258. package/.cursor/skills/leniu-architecture-design/SKILL.md +598 -0
  259. package/.cursor/skills/leniu-backend-annotations/SKILL.md +664 -0
  260. package/.cursor/skills/leniu-code-patterns/SKILL.md +365 -0
  261. package/.cursor/skills/leniu-crud-development/SKILL.md +1110 -0
  262. package/.cursor/skills/leniu-data-permission/SKILL.md +256 -0
  263. package/.cursor/skills/leniu-database-ops/SKILL.md +426 -0
  264. package/.cursor/skills/leniu-error-handler/SKILL.md +462 -0
  265. package/.cursor/skills/leniu-java-amount-handling/SKILL.md +461 -0
  266. package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
  267. package/.cursor/skills/leniu-java-concurrent/SKILL.md +400 -0
  268. package/.cursor/skills/leniu-java-entity/SKILL.md +751 -0
  269. package/.cursor/skills/leniu-java-export/SKILL.md +560 -0
  270. package/.cursor/skills/leniu-java-logging/SKILL.md +832 -0
  271. package/.cursor/skills/leniu-java-mq/SKILL.md +338 -0
  272. package/.cursor/skills/leniu-java-mybatis/SKILL.md +640 -0
  273. package/.cursor/skills/leniu-java-report-query-param/SKILL.md +291 -0
  274. package/.cursor/skills/leniu-java-task/SKILL.md +367 -0
  275. package/.cursor/skills/leniu-java-total-line/SKILL.md +195 -0
  276. package/.cursor/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  277. package/.cursor/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  278. package/.cursor/skills/leniu-mealtime/SKILL.md +215 -0
  279. package/.cursor/skills/leniu-redis-cache/SKILL.md +316 -0
  280. package/.cursor/skills/leniu-security-guard/SKILL.md +520 -0
  281. package/.cursor/skills/leniu-utils-toolkit/SKILL.md +380 -0
  282. package/.cursor/skills/openspec-apply-change/SKILL.md +156 -0
  283. package/.cursor/skills/openspec-archive-change/SKILL.md +114 -0
  284. package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  285. package/.cursor/skills/openspec-continue-change/SKILL.md +118 -0
  286. package/.cursor/skills/openspec-explore/SKILL.md +290 -0
  287. package/.cursor/skills/openspec-ff-change/SKILL.md +101 -0
  288. package/.cursor/skills/openspec-new-change/SKILL.md +74 -0
  289. package/.cursor/skills/openspec-onboard/SKILL.md +529 -0
  290. package/.cursor/skills/openspec-sync-specs/SKILL.md +138 -0
  291. package/.cursor/skills/openspec-verify-change/SKILL.md +168 -0
  292. package/.cursor/skills/performance-doctor/SKILL.md +627 -0
  293. package/.cursor/skills/project-navigator/SKILL.md +305 -0
  294. package/.cursor/skills/redis-cache/SKILL.md +839 -0
  295. package/.cursor/skills/scheduled-jobs/SKILL.md +633 -0
  296. package/.cursor/skills/security-guard/SKILL.md +748 -0
  297. package/.cursor/skills/sms-mail/SKILL.md +766 -0
  298. package/.cursor/skills/social-login/SKILL.md +668 -0
  299. package/.cursor/skills/store-pc/SKILL.md +366 -0
  300. package/.cursor/skills/task-tracker/SKILL.md +307 -0
  301. package/.cursor/skills/tech-decision/SKILL.md +393 -0
  302. package/.cursor/skills/tenant-management/SKILL.md +603 -0
  303. package/.cursor/skills/test-development/SKILL.md +755 -0
  304. package/.cursor/skills/ui-pc/SKILL.md +438 -0
  305. package/.cursor/skills/utils-toolkit/SKILL.md +615 -0
  306. package/.cursor/skills/websocket-sse/SKILL.md +716 -0
  307. package/.cursor/skills/workflow-engine/SKILL.md +676 -0
  308. package/AGENTS.md +669 -0
  309. package/CLAUDE.md +205 -0
  310. package/README.md +205 -0
  311. package/bin/index.js +179 -0
  312. package/init.sh +178 -0
  313. package/package.json +27 -0
@@ -0,0 +1,748 @@
1
+ ---
2
+ name: security-guard
3
+ description: |
4
+ 后端安全开发规范。包含 Sa-Token 认证授权、数据脱敏、数据加密、接口安全、漏洞防护。
5
+
6
+ 触发场景:
7
+ - Sa-Token 权限控制配置
8
+ - 登录认证、Token 管理
9
+ - 数据脱敏处理(@Sensitive)
10
+ - 数据加密处理(@EncryptField、@ApiEncrypt)
11
+ - 接口限流(@RateLimiter)
12
+ - 防重复提交(@RepeatSubmit)
13
+ - XSS/SQL注入防护
14
+
15
+ 触发词:安全、Sa-Token、@SaCheckPermission、@SaCheckLogin、@SaCheckRole、登录认证、Token、数据脱敏、@Sensitive、加密解密、@EncryptField、@ApiEncrypt、限流、@RateLimiter、防重复、@RepeatSubmit、XSS、SQL注入、漏洞防护、敏感数据、LoginHelper
16
+
17
+ 注意:
18
+ - 如需行级数据权限(@DataPermission、部门隔离),请使用 data-permission。
19
+ - 如果是设计异常处理机制(try-catch、错误码),请使用 error-handler。
20
+ ---
21
+
22
+ # 后端安全开发指南
23
+
24
+ > 本项目是纯后端项目,本文档专注于 Java 后端安全规范。
25
+
26
+ ## 1. Sa-Token 认证授权
27
+
28
+ ### 1.1 后端权限注解
29
+
30
+ ```java
31
+ import cn.dev33.satoken.annotation.*;
32
+ import cn.dev33.satoken.stp.StpUtil;
33
+
34
+ // 登录校验
35
+ @SaCheckLogin
36
+ @GetMapping("/userInfo")
37
+ public R<UserVo> getUserInfo() { }
38
+
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
+ // 多权限校验(满足其一)
50
+ @SaCheckPermission(value = {"system:user:add", "system:user:update"}, mode = SaMode.OR)
51
+
52
+ // 多权限校验(全部满足)
53
+ @SaCheckPermission(value = {"system:user:add", "system:user:update"}, mode = SaMode.AND)
54
+
55
+ // 多角色校验(满足其一)
56
+ @SaCheckRole(value = {"admin", "editor"}, mode = SaMode.OR)
57
+
58
+ // 二级认证(敏感操作需要再次验证)
59
+ @SaCheckSafe
60
+ ```
61
+
62
+ ### 1.2 LoginHelper 工具类(核心)
63
+
64
+ > **位置**:`ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java`
65
+
66
+ ```java
67
+ import org.dromara.common.satoken.utils.LoginHelper;
68
+
69
+ // ==================== 用户信息获取 ====================
70
+
71
+ // 获取当前登录用户完整信息
72
+ 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
+ // 用户登录(支持多设备类型)
113
+ LoginHelper.login(loginUser, loginParameter);
114
+ ```
115
+
116
+ ### 1.3 角色与权限常量
117
+
118
+ | 常量 | 值 | 说明 |
119
+ |------|-----|------|
120
+ | 超级管理员角色 | `superadmin` | 拥有所有权限 |
121
+ | 租户管理员角色 | `admin` | 租户内所有权限 |
122
+ | 通配符权限 | `*:*:*` | 拥有所有权限标识 |
123
+ | 超级管理员ID | `1L` | 系统超管用户ID |
124
+
125
+ ---
126
+
127
+ ## 2. 数据脱敏(@Sensitive)
128
+
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 基本用法
154
+
155
+ ```java
156
+ import org.dromara.common.sensitive.annotation.Sensitive;
157
+ import org.dromara.common.sensitive.core.SensitiveStrategy;
158
+
159
+ public class UserVo {
160
+
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
172
+
173
+ // 银行卡脱敏
174
+ @Sensitive(strategy = SensitiveStrategy.BANK_CARD)
175
+ private String bankCard; // 6222***********1234
176
+
177
+ // 中文姓名脱敏
178
+ @Sensitive(strategy = SensitiveStrategy.CHINESE_NAME)
179
+ private String realName; // 张*
180
+
181
+ // 地址脱敏
182
+ @Sensitive(strategy = SensitiveStrategy.ADDRESS)
183
+ private String address; // 北京市朝阳区****
184
+
185
+ // 密码脱敏(显示为******)
186
+ @Sensitive(strategy = SensitiveStrategy.PASSWORD)
187
+ 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
+ }
197
+ ```
198
+
199
+ ### 2.3 基于角色/权限的脱敏控制
200
+
201
+ ```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
+ }
229
+ ```
230
+
231
+ ### 2.4 日志脱敏
232
+
233
+ ```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));
242
+ ```
243
+
244
+ ---
245
+
246
+ ## 3. 数据加密(@EncryptField / @ApiEncrypt)
247
+
248
+ > **位置**:`ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/`
249
+
250
+ ### 3.1 支持的加密算法(5种)
251
+
252
+ | 算法 | 枚举值 | 类型 | 密钥要求 | 说明 |
253
+ |------|--------|------|---------|------|
254
+ | BASE64 | `BASE64` | 编码 | 无 | 仅编码,不是加密 |
255
+ | AES | `AES` | 对称加密 | 16/24/32 位 | 标准对称加密 |
256
+ | RSA | `RSA` | 非对称加密 | 公钥/私钥 | 标准非对称加密 |
257
+ | SM2 | `SM2` | 国密非对称 | 公钥/私钥 | 国密算法 |
258
+ | SM4 | `SM4` | 国密对称 | 16 位 | 国密算法 |
259
+
260
+ ### 3.2 字段级加密 @EncryptField
261
+
262
+ ```java
263
+ import org.dromara.common.encrypt.annotation.EncryptField;
264
+ import org.dromara.common.encrypt.enumd.AlgorithmType;
265
+ import org.dromara.common.encrypt.enumd.EncodeType;
266
+
267
+ public class User {
268
+
269
+ // 默认加密(使用全局配置)
270
+ @EncryptField
271
+ private String password;
272
+
273
+ // 指定 AES 加密
274
+ @EncryptField(algorithm = AlgorithmType.AES)
275
+ private String idCard;
276
+
277
+ // 指定 SM4 国密加密
278
+ @EncryptField(algorithm = AlgorithmType.SM4)
279
+ private String phone;
280
+
281
+ // 指定编码方式
282
+ @EncryptField(algorithm = AlgorithmType.AES, encode = EncodeType.HEX)
283
+ private String bankCard;
284
+ }
285
+ ```
286
+
287
+ ### 3.3 API 级加密 @ApiEncrypt
288
+
289
+ ```java
290
+ import org.dromara.common.encrypt.annotation.ApiEncrypt;
291
+
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
+ ```
338
+
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
356
+ ```
357
+
358
+ ---
359
+
360
+ ## 4. 接口限流(@RateLimiter)
361
+
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 使用示例
373
+
374
+ ```java
375
+ import org.dromara.common.ratelimiter.annotation.RateLimiter;
376
+ import org.dromara.common.ratelimiter.enums.LimitType;
377
+
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
+ ```
412
+
413
+ ### 4.3 注解参数
414
+
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 超时时间(秒) |
423
+
424
+ ### 4.4 推荐配置
425
+
426
+ | 场景 | time | count | limitType |
427
+ |------|------|-------|-----------|
428
+ | 登录接口 | 60 | 5-10 | IP |
429
+ | 验证码 | 60 | 3 | IP |
430
+ | 查询接口 | 60 | 100-1000 | DEFAULT |
431
+ | 写入接口 | 60 | 10-50 | DEFAULT |
432
+ | 敏感操作 | 60 | 1-5 | IP |
433
+
434
+ ---
435
+
436
+ ## 5. 防重复提交(@RepeatSubmit)
437
+
438
+ > **位置**:`ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/`
439
+
440
+ ### 5.1 使用示例
441
+
442
+ ```java
443
+ 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
+
471
+ ### 5.2 注解参数
472
+
473
+ | 参数 | 类型 | 默认值 | 说明 |
474
+ |------|------|--------|------|
475
+ | `interval` | int | `5000` | 间隔时间 |
476
+ | `timeUnit` | TimeUnit | `MILLISECONDS` | 时间单位 |
477
+ | `message` | String | 国际化 key | 错误提示消息 |
478
+
479
+ ### 5.3 推荐配置
480
+
481
+ | 场景 | 推荐间隔 |
482
+ |------|---------|
483
+ | 普通表单 | 3-5 秒 |
484
+ | 订单创建 | 10 秒 |
485
+ | 支付操作 | 30 秒 |
486
+ | 文件上传 | 10 秒 |
487
+
488
+ ---
489
+
490
+ ## 6. 数据权限(@DataPermission)
491
+
492
+ > **完整指南请参考 `data-permission` 技能**,本节仅作简要说明。
493
+
494
+ 数据权限是行级数据过滤机制,通过 MyBatis 拦截器自动注入 WHERE 条件。
495
+
496
+ ### 快速示例
497
+
498
+ ```java
499
+ @DataPermission({
500
+ @DataColumn(key = "deptName", value = "create_dept"),
501
+ @DataColumn(key = "userName", value = "create_by")
502
+ })
503
+ @Override
504
+ public TableDataInfo<OrderVo> pageWithPermission(OrderBo bo, PageQuery pageQuery) {
505
+ return page(buildQueryWrapper(bo), pageQuery).convert(OrderVo.class);
506
+ }
507
+ ```
508
+
509
+ ### 权限类型
510
+
511
+ | 类型 | 说明 |
512
+ |------|------|
513
+ | 全部数据 | 无过滤 |
514
+ | 本部门 | 只看本部门 |
515
+ | 本部门及以下 | 本部门 + 子部门 |
516
+ | 仅本人 | 只看自己创建的 |
517
+ | 自定义 | 按角色关联的部门 |
518
+
519
+ **更多内容**(表别名配置、临时忽略、扩展自定义类型、问题排查)请使用 `data-permission` 技能。
520
+
521
+ ---
522
+
523
+ ## 7. 输入校验
524
+
525
+ ```java
526
+ import jakarta.validation.constraints.*;
527
+ import org.dromara.common.core.validate.AddGroup;
528
+ import org.dromara.common.core.validate.EditGroup;
529
+
530
+ public class UserBo {
531
+
532
+ @NotNull(message = "ID不能为空", groups = { EditGroup.class })
533
+ private Long id;
534
+
535
+ @NotBlank(message = "用户名不能为空", groups = { AddGroup.class, EditGroup.class })
536
+ @Size(min = 2, max = 20, message = "用户名长度2-20")
537
+ @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母数字下划线")
538
+ 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
+ }
550
+
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) { }
557
+ ```
558
+
559
+ ---
560
+
561
+ ## 8. 常见漏洞防护
562
+
563
+ ### 8.1 SQL 注入防护
564
+
565
+ ```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");
597
+ ```
598
+
599
+ ### 8.3 越权访问防护
600
+
601
+ ```java
602
+ // ✅ 必须校验数据归属(三层架构:Service 直接调用 Mapper)
603
+ @Override
604
+ public OrderVo selectById(Long id) {
605
+ Order order = baseMapper.selectById(id);
606
+ if (ObjectUtil.isNull(order)) {
607
+ throw new ServiceException("订单不存在");
608
+ }
609
+
610
+ // 超管可以查看所有数据
611
+ if (LoginHelper.isSuperAdmin()) {
612
+ return MapstructUtils.convert(order, OrderVo.class);
613
+ }
614
+
615
+ // 校验数据归属
616
+ if (!order.getUserId().equals(LoginHelper.getUserId())) {
617
+ throw new ServiceException("无权访问此订单");
618
+ }
619
+
620
+ return MapstructUtils.convert(order, OrderVo.class);
621
+ }
622
+
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
+ }
642
+ ```
643
+
644
+ ### 8.4 文件上传安全
645
+
646
+ ```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
+ }
706
+ ```
707
+
708
+ ---
709
+
710
+ ## 9. 安全检查清单
711
+
712
+ ### 代码审查
713
+
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 设置了密码?
740
+
741
+ ---
742
+
743
+
744
+
745
+ ## 注意事项
746
+
747
+ - 如果需要 leniu-tengyun-core 项目的安全开发规范,请使用 `leniu-security-guard` skill
748
+ - leniu-tengyun-core 使用的是自研 secure 模块,注解和工具类与 RuoYi-Vue-Plus 不同