ai-engineering-init 1.7.0 → 1.8.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.
- package/.claude/hooks/skill-forced-eval.js +46 -62
- package/.claude/settings.json +10 -1
- package/.claude/skills/api-development/SKILL.md +179 -130
- package/.claude/skills/architecture-design/SKILL.md +102 -212
- package/.claude/skills/backend-annotations/SKILL.md +166 -220
- package/.claude/skills/bug-detective/SKILL.md +225 -186
- package/.claude/skills/code-patterns/SKILL.md +127 -244
- package/.claude/skills/collaborating-with-codex/SKILL.md +96 -113
- package/.claude/skills/crud-development/SKILL.md +226 -307
- package/.claude/skills/data-permission/SKILL.md +131 -202
- package/.claude/skills/database-ops/SKILL.md +158 -355
- package/.claude/skills/error-handler/SKILL.md +224 -285
- package/.claude/skills/file-oss-management/SKILL.md +174 -169
- package/.claude/skills/git-workflow/SKILL.md +123 -341
- package/.claude/skills/json-serialization/SKILL.md +121 -137
- package/.claude/skills/performance-doctor/SKILL.md +83 -89
- package/.claude/skills/redis-cache/SKILL.md +134 -185
- package/.claude/skills/scheduled-jobs/SKILL.md +187 -224
- package/.claude/skills/security-guard/SKILL.md +168 -276
- package/.claude/skills/sms-mail/SKILL.md +266 -228
- package/.claude/skills/social-login/SKILL.md +257 -195
- package/.claude/skills/tenant-management/SKILL.md +172 -188
- package/.claude/skills/utils-toolkit/SKILL.md +214 -222
- package/.claude/skills/websocket-sse/SKILL.md +251 -172
- package/.claude/skills/workflow-engine/SKILL.md +178 -250
- package/.codex/skills/api-development/SKILL.md +179 -130
- package/.codex/skills/architecture-design/SKILL.md +102 -212
- package/.codex/skills/backend-annotations/SKILL.md +166 -220
- package/.codex/skills/bug-detective/SKILL.md +225 -186
- package/.codex/skills/code-patterns/SKILL.md +127 -244
- package/.codex/skills/collaborating-with-codex/SKILL.md +96 -113
- package/.codex/skills/crud-development/SKILL.md +226 -307
- package/.codex/skills/data-permission/SKILL.md +131 -202
- package/.codex/skills/database-ops/SKILL.md +158 -355
- package/.codex/skills/error-handler/SKILL.md +224 -285
- package/.codex/skills/file-oss-management/SKILL.md +174 -169
- package/.codex/skills/git-workflow/SKILL.md +123 -341
- package/.codex/skills/json-serialization/SKILL.md +121 -137
- package/.codex/skills/performance-doctor/SKILL.md +83 -89
- package/.codex/skills/redis-cache/SKILL.md +134 -185
- package/.codex/skills/scheduled-jobs/SKILL.md +187 -224
- package/.codex/skills/security-guard/SKILL.md +168 -276
- package/.codex/skills/sms-mail/SKILL.md +266 -228
- package/.codex/skills/social-login/SKILL.md +257 -195
- package/.codex/skills/tenant-management/SKILL.md +172 -188
- package/.codex/skills/utils-toolkit/SKILL.md +214 -222
- package/.codex/skills/websocket-sse/SKILL.md +251 -172
- package/.codex/skills/workflow-engine/SKILL.md +178 -250
- package/.cursor/hooks/cursor-skill-eval.js +66 -6
- package/.cursor/skills/api-development/SKILL.md +179 -130
- package/.cursor/skills/architecture-design/SKILL.md +102 -212
- package/.cursor/skills/backend-annotations/SKILL.md +166 -220
- package/.cursor/skills/bug-detective/SKILL.md +225 -186
- package/.cursor/skills/code-patterns/SKILL.md +127 -244
- package/.cursor/skills/collaborating-with-codex/SKILL.md +96 -113
- package/.cursor/skills/crud-development/SKILL.md +226 -307
- package/.cursor/skills/data-permission/SKILL.md +131 -202
- package/.cursor/skills/database-ops/SKILL.md +158 -355
- package/.cursor/skills/error-handler/SKILL.md +224 -285
- package/.cursor/skills/file-oss-management/SKILL.md +174 -169
- package/.cursor/skills/git-workflow/SKILL.md +123 -341
- package/.cursor/skills/json-serialization/SKILL.md +121 -137
- package/.cursor/skills/performance-doctor/SKILL.md +83 -89
- package/.cursor/skills/redis-cache/SKILL.md +134 -185
- package/.cursor/skills/scheduled-jobs/SKILL.md +187 -224
- package/.cursor/skills/security-guard/SKILL.md +168 -276
- package/.cursor/skills/sms-mail/SKILL.md +266 -228
- package/.cursor/skills/social-login/SKILL.md +257 -195
- package/.cursor/skills/tenant-management/SKILL.md +172 -188
- package/.cursor/skills/utils-toolkit/SKILL.md +214 -222
- package/.cursor/skills/websocket-sse/SKILL.md +251 -172
- package/.cursor/skills/workflow-engine/SKILL.md +178 -250
- package/AGENTS.md +49 -540
- package/CLAUDE.md +73 -119
- package/README.md +37 -6
- package/bin/index.js +5 -1
- package/package.json +1 -1
- package/src/skills/api-development/SKILL.md +179 -130
- package/src/skills/architecture-design/SKILL.md +102 -212
- package/src/skills/backend-annotations/SKILL.md +166 -220
- package/src/skills/bug-detective/SKILL.md +225 -186
- package/src/skills/code-patterns/SKILL.md +127 -244
- package/src/skills/collaborating-with-codex/SKILL.md +96 -113
- package/src/skills/crud-development/SKILL.md +226 -307
- package/src/skills/data-permission/SKILL.md +131 -202
- package/src/skills/database-ops/SKILL.md +158 -355
- package/src/skills/error-handler/SKILL.md +224 -285
- package/src/skills/file-oss-management/SKILL.md +174 -169
- package/src/skills/git-workflow/SKILL.md +123 -341
- package/src/skills/json-serialization/SKILL.md +121 -137
- package/src/skills/performance-doctor/SKILL.md +83 -89
- package/src/skills/redis-cache/SKILL.md +134 -185
- package/src/skills/scheduled-jobs/SKILL.md +187 -224
- package/src/skills/security-guard/SKILL.md +168 -276
- package/src/skills/sms-mail/SKILL.md +266 -228
- package/src/skills/social-login/SKILL.md +257 -195
- package/src/skills/tenant-management/SKILL.md +172 -188
- package/src/skills/utils-toolkit/SKILL.md +214 -222
- package/src/skills/websocket-sse/SKILL.md +251 -172
- package/src/skills/workflow-engine/SKILL.md +178 -250
- package/.claude/skills/skill-creator/LICENSE.txt +0 -202
- package/.claude/skills/skill-creator/SKILL.md +0 -479
- package/.claude/skills/skill-creator/agents/analyzer.md +0 -274
- package/.claude/skills/skill-creator/agents/comparator.md +0 -202
- package/.claude/skills/skill-creator/agents/grader.md +0 -223
- package/.claude/skills/skill-creator/assets/eval_review.html +0 -146
- package/.claude/skills/skill-creator/eval-viewer/generate_review.py +0 -471
- package/.claude/skills/skill-creator/eval-viewer/viewer.html +0 -1325
- package/.claude/skills/skill-creator/references/schemas.md +0 -430
- package/.claude/skills/skill-creator/scripts/__init__.py +0 -0
- package/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
- package/.claude/skills/skill-creator/scripts/generate_report.py +0 -326
- package/.claude/skills/skill-creator/scripts/improve_description.py +0 -248
- package/.claude/skills/skill-creator/scripts/package_skill.py +0 -136
- package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -103
- package/.claude/skills/skill-creator/scripts/run_eval.py +0 -310
- package/.claude/skills/skill-creator/scripts/run_loop.py +0 -332
- package/.claude/skills/skill-creator/scripts/utils.py +0 -47
|
@@ -1,353 +1,245 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: security-guard
|
|
3
3
|
description: |
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
通用后端安全开发指南。包含认证授权模式对比、输入校验、XSS/SQL注入防护、数据脱敏加密、安全检查清单。
|
|
6
5
|
触发场景:
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
-
|
|
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。
|
|
6
|
+
- 设计认证授权方案
|
|
7
|
+
- 配置权限校验
|
|
8
|
+
- 防护 XSS / SQL 注入
|
|
9
|
+
- 数据脱敏 / 加密处理
|
|
10
|
+
- 安全合规审查
|
|
11
|
+
触发词:安全、认证、授权、权限、Token、登录、XSS、SQL注入、脱敏、加密、RBAC、OAuth、CORS、漏洞防护
|
|
12
|
+
注意:如果项目有专属技能(如 `leniu-security-guard`),优先使用专属版本。
|
|
20
13
|
---
|
|
21
14
|
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
> 本项目是纯后端项目,本文档专注于 Java 后端安全规范。
|
|
25
|
-
|
|
26
|
-
## 1. Sa-Token 认证授权
|
|
27
|
-
|
|
28
|
-
### 1.1 权限注解
|
|
29
|
-
|
|
30
|
-
```java
|
|
31
|
-
import cn.dev33.satoken.annotation.*;
|
|
32
|
-
|
|
33
|
-
@SaCheckLogin // 登录校验
|
|
34
|
-
@SaCheckPermission("system:user:add") // 权限校验
|
|
35
|
-
@SaCheckRole("admin") // 角色校验
|
|
36
|
-
@SaCheckSafe // 二级认证(敏感操作)
|
|
37
|
-
|
|
38
|
-
// 多权限(满足其一 / 全部满足)
|
|
39
|
-
@SaCheckPermission(value = {"system:user:add", "system:user:update"}, mode = SaMode.OR)
|
|
40
|
-
@SaCheckPermission(value = {"system:user:add", "system:user:update"}, mode = SaMode.AND)
|
|
41
|
-
|
|
42
|
-
// 多角色(满足其一)
|
|
43
|
-
@SaCheckRole(value = {"admin", "editor"}, mode = SaMode.OR)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### 1.2 LoginHelper 工具类
|
|
47
|
-
|
|
48
|
-
> 位置:`ruoyi-common-satoken/.../utils/LoginHelper.java`
|
|
15
|
+
# 安全开发指南
|
|
49
16
|
|
|
50
|
-
|
|
51
|
-
import org.dromara.common.satoken.utils.LoginHelper;
|
|
52
|
-
|
|
53
|
-
// 用户信息
|
|
54
|
-
LoginUser user = LoginHelper.getLoginUser();
|
|
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();
|
|
67
|
-
LoginHelper.login(loginUser, loginParameter);
|
|
68
|
-
```
|
|
17
|
+
> 通用模板。如果项目有专属技能(如 `leniu-security-guard`),优先使用。
|
|
69
18
|
|
|
70
|
-
|
|
19
|
+
## 设计原则
|
|
71
20
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
| 超级管理员ID | `1L` | 系统超管用户ID |
|
|
21
|
+
1. **纵深防御**:不依赖单一安全机制,从传输层、认证层、授权层、数据层多维度防护。
|
|
22
|
+
2. **最小权限**:默认拒绝,显式授权。每个接口必须声明所需权限。
|
|
23
|
+
3. **输入不可信**:所有外部输入(用户参数、请求头、Cookie)必须校验后使用。
|
|
24
|
+
4. **敏感数据保护**:存储加密、传输加密、展示脱敏,三层保护。
|
|
25
|
+
5. **审计可追溯**:关键操作记录日志,包含操作人、操作时间、操作内容。
|
|
78
26
|
|
|
79
27
|
---
|
|
80
28
|
|
|
81
|
-
##
|
|
82
|
-
|
|
83
|
-
> 位置:`ruoyi-common-sensitive/.../`
|
|
84
|
-
> 完整 17 种策略详见 `references/sensitive-strategies.md`
|
|
85
|
-
|
|
86
|
-
### 基本用法
|
|
87
|
-
|
|
88
|
-
```java
|
|
89
|
-
import org.dromara.common.sensitive.annotation.Sensitive;
|
|
90
|
-
import org.dromara.common.sensitive.core.SensitiveStrategy;
|
|
91
|
-
|
|
92
|
-
public class UserVo {
|
|
93
|
-
@Sensitive(strategy = SensitiveStrategy.PHONE) // 138****8888
|
|
94
|
-
private String phone;
|
|
95
|
-
|
|
96
|
-
@Sensitive(strategy = SensitiveStrategy.ID_CARD) // 110***********1234
|
|
97
|
-
private String idCard;
|
|
98
|
-
|
|
99
|
-
@Sensitive(strategy = SensitiveStrategy.EMAIL) // t**@example.com
|
|
100
|
-
private String email;
|
|
101
|
-
|
|
102
|
-
@Sensitive(strategy = SensitiveStrategy.BANK_CARD) // 6222***********1234
|
|
103
|
-
private String bankCard;
|
|
29
|
+
## 认证授权方案对比
|
|
104
30
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
### 基于角色/权限的脱敏控制
|
|
114
|
-
|
|
115
|
-
```java
|
|
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;
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 日志脱敏
|
|
131
|
-
|
|
132
|
-
```java
|
|
133
|
-
// NG: log.info("手机号: {}", phone);
|
|
134
|
-
// OK:
|
|
135
|
-
log.info("手机号: {}", DesensitizedUtil.mobilePhone(phone));
|
|
136
|
-
```
|
|
31
|
+
| 维度 | Spring Security | Apache Shiro | Sa-Token |
|
|
32
|
+
|------|----------------|-------------|----------|
|
|
33
|
+
| 学习曲线 | 陡峭 | 中等 | 平缓 |
|
|
34
|
+
| 功能丰富度 | 最全面 | 基础够用 | 功能丰富 |
|
|
35
|
+
| Spring 集成 | 原生集成 | 需适配 | 自动配置 |
|
|
36
|
+
| OAuth2 支持 | 原生支持 | 需扩展 | 内置 SSO |
|
|
37
|
+
| 微服务支持 | 优秀 | 一般 | 良好 |
|
|
38
|
+
| 适用场景 | 企业级、复杂权限 | 轻量项目 | 快速开发 |
|
|
137
39
|
|
|
138
40
|
---
|
|
139
41
|
|
|
140
|
-
##
|
|
141
|
-
|
|
142
|
-
> 位置:`ruoyi-common-encrypt/.../`
|
|
143
|
-
> 完整加密配置和工具类详见 `references/encrypt-config.md`
|
|
42
|
+
## 实现模式
|
|
144
43
|
|
|
145
|
-
###
|
|
146
|
-
|
|
147
|
-
| 算法 | 类型 | 密钥要求 |
|
|
148
|
-
|------|------|---------|
|
|
149
|
-
| BASE64 | 编码 | 无 |
|
|
150
|
-
| AES | 对称加密 | 16/24/32 位 |
|
|
151
|
-
| RSA | 非对称加密 | 公钥/私钥 |
|
|
152
|
-
| SM2 | 国密非对称 | 公钥/私钥 |
|
|
153
|
-
| SM4 | 国密对称 | 16 位 |
|
|
154
|
-
|
|
155
|
-
### 字段级加密
|
|
44
|
+
### 1. 认证(Authentication)
|
|
156
45
|
|
|
157
46
|
```java
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
47
|
+
// 方式一:注解式认证(框架无关的概念)
|
|
48
|
+
@[你的认证注解] // 登录校验
|
|
49
|
+
@[你的权限注解]("system:user:add") // 权限校验
|
|
50
|
+
@[你的角色注解]("admin") // 角色校验
|
|
51
|
+
|
|
52
|
+
// 方式二:编程式认证
|
|
53
|
+
@RestController
|
|
54
|
+
public class UserController {
|
|
55
|
+
|
|
56
|
+
@Autowired
|
|
57
|
+
private [你的安全工具类] securityUtils;
|
|
58
|
+
|
|
59
|
+
@GetMapping("/profile")
|
|
60
|
+
public Result<?> profile() {
|
|
61
|
+
// 获取当前登录用户
|
|
62
|
+
Long userId = securityUtils.getCurrentUserId();
|
|
63
|
+
String username = securityUtils.getCurrentUsername();
|
|
64
|
+
boolean isAdmin = securityUtils.hasRole("admin");
|
|
65
|
+
boolean hasPerm = securityUtils.hasPermission("system:user:list");
|
|
66
|
+
|
|
67
|
+
if (!securityUtils.isAuthenticated()) {
|
|
68
|
+
throw new [你的异常类]("未登录");
|
|
69
|
+
}
|
|
70
|
+
// ...
|
|
71
|
+
}
|
|
170
72
|
}
|
|
171
73
|
```
|
|
172
74
|
|
|
173
|
-
###
|
|
75
|
+
### 2. 授权模型(RBAC)
|
|
174
76
|
|
|
175
|
-
```java
|
|
176
|
-
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
|
177
|
-
|
|
178
|
-
@ApiEncrypt // 请求体自动解密
|
|
179
|
-
@PostMapping("/addUser")
|
|
180
|
-
public R<Long> addUser(@RequestBody UserBo bo) { }
|
|
181
|
-
|
|
182
|
-
@ApiEncrypt(response = true) // 请求解密 + 响应加密
|
|
183
|
-
@PostMapping("/updateUser")
|
|
184
|
-
public R<Void> updateUser(@RequestBody UserBo bo) { }
|
|
185
77
|
```
|
|
78
|
+
用户 (User)
|
|
79
|
+
└── 角色 (Role) -- 多对多
|
|
80
|
+
└── 权限 (Permission) -- 多对多
|
|
81
|
+
└── 菜单/按钮/API
|
|
186
82
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
> 位置:`ruoyi-common-ratelimiter/.../`
|
|
83
|
+
权限标识格式:模块:资源:操作
|
|
84
|
+
示例:system:user:add, order:info:export
|
|
85
|
+
```
|
|
192
86
|
|
|
193
87
|
```java
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// 全局限流:60秒内最多100次
|
|
198
|
-
@RateLimiter(time = 60, count = 100)
|
|
199
|
-
|
|
200
|
-
// IP 限流:每个 IP 每分钟最多10次
|
|
201
|
-
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
|
|
202
|
-
|
|
203
|
-
// 动态 key(SpEL)
|
|
204
|
-
@RateLimiter(key = "#userId", time = 60, count = 5)
|
|
205
|
-
|
|
206
|
-
// 自定义错误消息
|
|
207
|
-
@RateLimiter(time = 60, count = 10, message = "访问过于频繁,请稍后再试")
|
|
88
|
+
// 多权限校验(满足其一 / 全部满足)
|
|
89
|
+
@RequiresPermissions(value = {"system:user:add", "system:user:edit"}, logical = Logical.OR)
|
|
90
|
+
@RequiresPermissions(value = {"system:user:add", "system:user:edit"}, logical = Logical.AND)
|
|
208
91
|
|
|
209
|
-
//
|
|
210
|
-
@
|
|
92
|
+
// 多角色校验
|
|
93
|
+
@RequiresRoles(value = {"admin", "editor"}, logical = Logical.OR)
|
|
211
94
|
```
|
|
212
95
|
|
|
213
|
-
###
|
|
214
|
-
|
|
215
|
-
| 场景 | time | count | limitType |
|
|
216
|
-
|------|------|-------|-----------|
|
|
217
|
-
| 登录接口 | 60 | 5-10 | IP |
|
|
218
|
-
| 验证码 | 60 | 3 | IP |
|
|
219
|
-
| 查询接口 | 60 | 100-1000 | DEFAULT |
|
|
220
|
-
| 写入接口 | 60 | 10-50 | DEFAULT |
|
|
221
|
-
| 敏感操作 | 60 | 1-5 | IP |
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## 5. 防重复提交(@RepeatSubmit)
|
|
226
|
-
|
|
227
|
-
> 位置:`ruoyi-common-idempotent/.../`
|
|
96
|
+
### 3. 输入校验
|
|
228
97
|
|
|
229
98
|
```java
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
@RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS) // 10 秒
|
|
234
|
-
@RepeatSubmit(interval = 5000, message = "请勿重复提交订单") // 自定义消息
|
|
235
|
-
```
|
|
99
|
+
public class UserDTO {
|
|
100
|
+
@NotNull(message = "ID不能为空", groups = {UpdateGroup.class})
|
|
101
|
+
private Long id;
|
|
236
102
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
| 支付操作 | 30 秒 |
|
|
242
|
-
| 文件上传 | 10 秒 |
|
|
103
|
+
@NotBlank(message = "用户名不能为空")
|
|
104
|
+
@Size(min = 2, max = 20, message = "用户名长度2-20")
|
|
105
|
+
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "只能包含字母数字下划线")
|
|
106
|
+
private String username;
|
|
243
107
|
|
|
244
|
-
|
|
108
|
+
@Email(message = "邮箱格式不正确")
|
|
109
|
+
private String email;
|
|
245
110
|
|
|
246
|
-
|
|
111
|
+
@Min(value = 0, message = "年龄不能为负")
|
|
112
|
+
@Max(value = 150, message = "年龄超出范围")
|
|
113
|
+
private Integer age;
|
|
114
|
+
}
|
|
247
115
|
|
|
248
|
-
|
|
116
|
+
// Controller 分组校验
|
|
117
|
+
@PostMapping
|
|
118
|
+
public Result<?> add(@Validated(AddGroup.class) @RequestBody UserDTO dto) { }
|
|
249
119
|
|
|
250
|
-
|
|
251
|
-
@
|
|
252
|
-
@DataColumn(key = "deptName", value = "create_dept"),
|
|
253
|
-
@DataColumn(key = "userName", value = "create_by")
|
|
254
|
-
})
|
|
255
|
-
public TableDataInfo<OrderVo> pageWithPermission(OrderBo bo, PageQuery pageQuery) { }
|
|
120
|
+
@PutMapping
|
|
121
|
+
public Result<?> update(@Validated(UpdateGroup.class) @RequestBody UserDTO dto) { }
|
|
256
122
|
```
|
|
257
123
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
## 7. 输入校验
|
|
124
|
+
### 4. 越权访问防护
|
|
263
125
|
|
|
264
126
|
```java
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
127
|
+
public [你的VO类] selectById(Long id) {
|
|
128
|
+
var entity = baseMapper.selectById(id);
|
|
129
|
+
if (entity == null) {
|
|
130
|
+
throw new [你的异常类]("数据不存在");
|
|
131
|
+
}
|
|
132
|
+
// 非管理员只能访问自己的数据
|
|
133
|
+
Long currentUserId = [你的安全工具类].getCurrentUserId();
|
|
134
|
+
if (![你的安全工具类].isAdmin() && !entity.getCreatedBy().equals(currentUserId)) {
|
|
135
|
+
throw new [你的异常类]("无权访问此数据");
|
|
136
|
+
}
|
|
137
|
+
return convertToVo(entity);
|
|
276
138
|
}
|
|
277
|
-
|
|
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) { }
|
|
281
139
|
```
|
|
282
140
|
|
|
283
141
|
---
|
|
284
142
|
|
|
285
|
-
##
|
|
143
|
+
## 常见漏洞防护
|
|
286
144
|
|
|
287
145
|
### SQL 注入
|
|
288
146
|
|
|
289
147
|
```java
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
148
|
+
// 禁止:字符串拼接 SQL
|
|
149
|
+
"SELECT * FROM user WHERE name = '" + name + "'"
|
|
150
|
+
@Select("SELECT * FROM user WHERE name = '${name}'")
|
|
151
|
+
|
|
152
|
+
// 正确:参数化查询
|
|
153
|
+
@Select("SELECT * FROM user WHERE name = #{name}")
|
|
154
|
+
// 或使用 ORM 框架的 QueryWrapper / LambdaQueryWrapper
|
|
294
155
|
```
|
|
295
156
|
|
|
296
|
-
###
|
|
157
|
+
### XSS 防护
|
|
297
158
|
|
|
298
159
|
```java
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
return MapstructUtils.convert(order, OrderVo.class);
|
|
160
|
+
// 方式一:全局 Filter(推荐)
|
|
161
|
+
@Bean
|
|
162
|
+
public FilterRegistrationBean<XssFilter> xssFilter() {
|
|
163
|
+
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
|
|
164
|
+
registration.setFilter(new XssFilter());
|
|
165
|
+
registration.addUrlPatterns("/*");
|
|
166
|
+
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
|
167
|
+
return registration;
|
|
309
168
|
}
|
|
310
169
|
|
|
311
|
-
//
|
|
170
|
+
// 方式二:手动转义
|
|
171
|
+
String safe = HtmlUtils.htmlEscape(userInput);
|
|
172
|
+
String safe = StringEscapeUtils.escapeHtml4(userInput);
|
|
312
173
|
```
|
|
313
174
|
|
|
314
175
|
### 敏感信息泄露
|
|
315
176
|
|
|
316
177
|
```java
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
178
|
+
// 禁止:直接返回 Entity(可能包含密码等)
|
|
179
|
+
return userDao.getById(id);
|
|
180
|
+
|
|
181
|
+
// 正确:使用 VO 过滤 + @Sensitive 脱敏
|
|
182
|
+
UserVo vo = new UserVo();
|
|
183
|
+
BeanUtils.copyProperties(user, vo);
|
|
184
|
+
return vo;
|
|
185
|
+
|
|
186
|
+
// 日志脱敏
|
|
187
|
+
log.info("手机号: {}", DesensitizedUtil.mobilePhone(phone));
|
|
320
188
|
```
|
|
321
189
|
|
|
322
190
|
---
|
|
323
191
|
|
|
324
|
-
##
|
|
192
|
+
## 安全检查清单
|
|
325
193
|
|
|
326
194
|
### 代码审查
|
|
327
195
|
|
|
328
|
-
- [ ] 用户输入经过 `@NotBlank
|
|
329
|
-
- [ ] SQL
|
|
330
|
-
- [ ]
|
|
331
|
-
- [ ]
|
|
332
|
-
- [ ] Controller
|
|
333
|
-
- [ ]
|
|
334
|
-
- [ ]
|
|
196
|
+
- [ ] 用户输入经过 `@NotBlank` / `@Size` / `@Pattern` 校验
|
|
197
|
+
- [ ] SQL 使用参数化查询(`#{}` 而非 `${}`)
|
|
198
|
+
- [ ] 敏感字段使用脱敏注解或 VO 过滤
|
|
199
|
+
- [ ] 存储敏感数据使用加密
|
|
200
|
+
- [ ] Controller 添加权限注解
|
|
201
|
+
- [ ] 写入接口添加防重复提交
|
|
202
|
+
- [ ] 高频接口添加限流
|
|
335
203
|
- [ ] 批量操作校验数据归属(防越权)
|
|
336
|
-
- [ ]
|
|
204
|
+
- [ ] 文件上传校验类型 / 大小 / 扩展名(白名单)
|
|
337
205
|
- [ ] 日志中无敏感信息(或已脱敏)
|
|
338
206
|
|
|
339
|
-
###
|
|
207
|
+
### 配置与部署
|
|
340
208
|
|
|
341
|
-
- [ ]
|
|
342
|
-
- [ ]
|
|
209
|
+
- [ ] 生产环境关闭调试模式、Swagger
|
|
210
|
+
- [ ] 敏感配置使用环境变量或配置中心加密
|
|
343
211
|
- [ ] Token 有效期合理(2-24h)
|
|
344
|
-
- [ ] CORS 不使用
|
|
345
|
-
- [ ] 启用 HTTPS
|
|
346
|
-
- [ ]
|
|
212
|
+
- [ ] CORS 不使用 `*`,限定域名
|
|
213
|
+
- [ ] 启用 HTTPS
|
|
214
|
+
- [ ] 设置安全响应头(X-Frame-Options、X-Content-Type-Options、CSP)
|
|
215
|
+
- [ ] 错误页不泄露堆栈信息
|
|
216
|
+
- [ ] 数据库 / Redis 端口不对外暴露
|
|
347
217
|
|
|
348
218
|
---
|
|
349
219
|
|
|
350
|
-
##
|
|
220
|
+
## 常见错误
|
|
221
|
+
|
|
222
|
+
```java
|
|
223
|
+
// 1. 忘记加认证注解,接口裸奔
|
|
224
|
+
@GetMapping("/users")
|
|
225
|
+
public Result<?> list() { ... } // 任何人可访问!
|
|
351
226
|
|
|
352
|
-
|
|
353
|
-
|
|
227
|
+
// 2. 只校验前端,不校验后端
|
|
228
|
+
// 前端限制了输入范围,但后端不做校验 -> 可被绕过
|
|
229
|
+
|
|
230
|
+
// 3. 用 Map 传递用户信息(类型不安全)
|
|
231
|
+
Map<String, Object> user = getCurrentUser(); // 缺乏类型约束
|
|
232
|
+
// 应使用强类型 LoginUser 对象
|
|
233
|
+
|
|
234
|
+
// 4. 密码明文存储
|
|
235
|
+
user.setPassword(rawPassword);
|
|
236
|
+
// 应使用 BCrypt / SCrypt 哈希
|
|
237
|
+
user.setPassword(passwordEncoder.encode(rawPassword));
|
|
238
|
+
|
|
239
|
+
// 5. Token 存储敏感信息
|
|
240
|
+
// JWT payload 不应包含密码、完整身份证号等
|
|
241
|
+
|
|
242
|
+
// 6. CORS 配置过于宽松
|
|
243
|
+
.allowedOrigins("*").allowedMethods("*").allowCredentials(true)
|
|
244
|
+
// allowedOrigins("*") 与 allowCredentials(true) 不能同时使用
|
|
245
|
+
```
|