ai-engineering-init 1.3.4 → 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.
- package/.claude/hooks/skill-forced-eval.js +2 -0
- package/.claude/settings.json +3 -3
- package/.claude/skills/add-skill/SKILL.md +79 -32
- package/.claude/skills/api-development/SKILL.md +83 -377
- package/.claude/skills/architecture-design/SKILL.md +138 -632
- package/.claude/skills/backend-annotations/SKILL.md +134 -506
- package/.claude/skills/banana-image/SKILL.md +10 -3
- package/.claude/skills/brainstorm/SKILL.md +103 -535
- package/.claude/skills/bug-detective/SKILL.md +147 -1097
- package/.claude/skills/bug-detective/references/error-patterns.md +242 -0
- package/.claude/skills/code-patterns/SKILL.md +116 -426
- package/.claude/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/.claude/skills/crud-development/SKILL.md +64 -304
- package/.claude/skills/data-permission/SKILL.md +105 -412
- package/.claude/skills/data-permission/references/custom-data-scope.md +90 -0
- package/.claude/skills/file-oss-management/SKILL.md +106 -714
- package/.claude/skills/file-oss-management/references/entities.md +105 -0
- package/.claude/skills/file-oss-management/references/service-impl.md +104 -0
- package/.claude/skills/leniu-api-development/SKILL.md +142 -626
- package/.claude/skills/leniu-api-development/references/real-examples.md +273 -0
- package/.claude/skills/leniu-architecture-design/SKILL.md +176 -391
- package/.claude/skills/leniu-backend-annotations/SKILL.md +132 -519
- package/.claude/skills/leniu-brainstorm/SKILL.md +132 -541
- package/.claude/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/.claude/skills/leniu-crud-development/SKILL.md +232 -938
- package/.claude/skills/leniu-crud-development/references/templates.md +597 -0
- package/.claude/skills/leniu-customization-location/SKILL.md +410 -0
- package/.claude/skills/leniu-data-permission/SKILL.md +70 -0
- package/.claude/skills/leniu-java-entity/SKILL.md +76 -590
- package/.claude/skills/leniu-java-entity/references/templates.md +237 -0
- package/.claude/skills/leniu-java-export/SKILL.md +94 -379
- package/.claude/skills/leniu-java-logging/SKILL.md +106 -709
- package/.claude/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/.claude/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/.claude/skills/leniu-java-mybatis/SKILL.md +73 -446
- package/.claude/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/.claude/skills/leniu-report-customization/SKILL.md +111 -365
- package/.claude/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/.claude/skills/leniu-report-standard-customization/SKILL.md +111 -334
- package/.claude/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/.claude/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/.claude/skills/leniu-security-guard/SKILL.md +133 -347
- package/.claude/skills/mysql-debug/SKILL.md +364 -0
- package/.claude/skills/openspec-apply-change/SKILL.md +10 -1
- package/.claude/skills/openspec-archive-change/SKILL.md +9 -1
- package/.claude/skills/openspec-bulk-archive-change/SKILL.md +9 -1
- package/.claude/skills/openspec-continue-change/SKILL.md +9 -1
- package/.claude/skills/openspec-explore/SKILL.md +10 -1
- package/.claude/skills/openspec-ff-change/SKILL.md +9 -1
- package/.claude/skills/openspec-new-change/SKILL.md +9 -1
- package/.claude/skills/openspec-onboard/SKILL.md +15 -130
- package/.claude/skills/openspec-sync-specs/SKILL.md +9 -1
- package/.claude/skills/openspec-verify-change/SKILL.md +9 -1
- package/.claude/skills/performance-doctor/SKILL.md +110 -434
- package/.claude/skills/redis-cache/SKILL.md +89 -595
- package/.claude/skills/redis-cache/references/listeners.md +23 -0
- package/.claude/skills/scheduled-jobs/SKILL.md +88 -407
- package/.claude/skills/security-guard/SKILL.md +137 -532
- package/.claude/skills/security-guard/references/encrypt-config.md +103 -0
- package/.claude/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/.claude/skills/sms-mail/SKILL.md +116 -574
- package/.claude/skills/sms-mail/references/mail-config.md +88 -0
- package/.claude/skills/sms-mail/references/sms-config.md +74 -0
- package/.claude/skills/social-login/SKILL.md +112 -514
- package/.claude/skills/social-login/references/provider-configs.md +118 -0
- package/.claude/skills/tenant-management/SKILL.md +129 -444
- package/.claude/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/.claude/skills/test-development/SKILL.md +86 -540
- package/.claude/skills/test-development/references/parameterized-examples.md +119 -0
- package/.claude/skills/utils-toolkit/SKILL.md +52 -305
- package/.claude/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/.claude/skills/websocket-sse/SKILL.md +105 -550
- package/.claude/skills/workflow-engine/SKILL.md +147 -502
- package/.codex/skills/add-skill/SKILL.md +79 -32
- package/.codex/skills/api-development/SKILL.md +172 -599
- package/.codex/skills/architecture-design/SKILL.md +138 -504
- package/.codex/skills/backend-annotations/SKILL.md +134 -496
- package/.codex/skills/banana-image/SKILL.md +10 -3
- package/.codex/skills/brainstorm/SKILL.md +103 -535
- package/.codex/skills/bug-detective/SKILL.md +147 -1097
- package/.codex/skills/bug-detective/references/error-patterns.md +242 -0
- package/.codex/skills/code-patterns/SKILL.md +120 -282
- package/.codex/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/.codex/skills/crud-development/SKILL.md +64 -292
- package/.codex/skills/data-permission/SKILL.md +108 -407
- package/.codex/skills/data-permission/references/custom-data-scope.md +90 -0
- package/.codex/skills/database-ops/SKILL.md +8 -154
- package/.codex/skills/error-handler/SKILL.md +10 -0
- package/.codex/skills/file-oss-management/SKILL.md +106 -714
- package/.codex/skills/file-oss-management/references/entities.md +105 -0
- package/.codex/skills/file-oss-management/references/service-impl.md +104 -0
- package/.codex/skills/git-workflow/SKILL.md +27 -5
- package/.codex/skills/leniu-api-development/SKILL.md +142 -626
- package/.codex/skills/leniu-api-development/references/real-examples.md +273 -0
- package/.codex/skills/leniu-architecture-design/SKILL.md +176 -391
- package/.codex/skills/leniu-backend-annotations/SKILL.md +132 -519
- package/.codex/skills/leniu-brainstorm/SKILL.md +132 -541
- package/.codex/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/.codex/skills/leniu-crud-development/SKILL.md +232 -938
- package/.codex/skills/leniu-crud-development/references/templates.md +597 -0
- package/.codex/skills/leniu-customization-location/SKILL.md +410 -0
- package/.codex/skills/leniu-data-permission/SKILL.md +70 -0
- package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
- package/.codex/skills/leniu-java-entity/SKILL.md +76 -590
- package/.codex/skills/leniu-java-entity/references/templates.md +237 -0
- package/.codex/skills/leniu-java-export/SKILL.md +94 -379
- package/.codex/skills/leniu-java-logging/SKILL.md +106 -709
- package/.codex/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/.codex/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/.codex/skills/leniu-java-mybatis/SKILL.md +73 -446
- package/.codex/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/.codex/skills/leniu-report-customization/SKILL.md +111 -365
- package/.codex/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/.codex/skills/leniu-report-standard-customization/SKILL.md +111 -334
- package/.codex/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/.codex/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/.codex/skills/leniu-security-guard/SKILL.md +133 -347
- package/.codex/skills/mysql-debug/SKILL.md +364 -0
- package/.codex/skills/openspec-apply-change/SKILL.md +10 -1
- package/.codex/skills/openspec-archive-change/SKILL.md +9 -1
- package/.codex/skills/openspec-bulk-archive-change/SKILL.md +9 -1
- package/.codex/skills/openspec-continue-change/SKILL.md +9 -1
- package/.codex/skills/openspec-explore/SKILL.md +10 -1
- package/.codex/skills/openspec-ff-change/SKILL.md +9 -1
- package/.codex/skills/openspec-new-change/SKILL.md +9 -1
- package/.codex/skills/openspec-onboard/SKILL.md +15 -130
- package/.codex/skills/openspec-sync-specs/SKILL.md +9 -1
- package/.codex/skills/openspec-verify-change/SKILL.md +9 -1
- package/.codex/skills/performance-doctor/SKILL.md +110 -434
- package/.codex/skills/project-navigator/SKILL.md +20 -1
- package/.codex/skills/redis-cache/SKILL.md +93 -589
- package/.codex/skills/redis-cache/references/listeners.md +23 -0
- package/.codex/skills/scheduled-jobs/SKILL.md +88 -407
- package/.codex/skills/security-guard/SKILL.md +141 -527
- package/.codex/skills/security-guard/references/encrypt-config.md +103 -0
- package/.codex/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/.codex/skills/sms-mail/SKILL.md +116 -574
- package/.codex/skills/sms-mail/references/mail-config.md +88 -0
- package/.codex/skills/sms-mail/references/sms-config.md +74 -0
- package/.codex/skills/social-login/SKILL.md +112 -514
- package/.codex/skills/social-login/references/provider-configs.md +118 -0
- package/.codex/skills/store-pc/SKILL.md +258 -383
- package/.codex/skills/tenant-management/SKILL.md +129 -444
- package/.codex/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/.codex/skills/test-development/SKILL.md +86 -540
- package/.codex/skills/test-development/references/parameterized-examples.md +119 -0
- package/.codex/skills/ui-pc/SKILL.md +350 -387
- package/.codex/skills/utils-toolkit/SKILL.md +52 -283
- package/.codex/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/.codex/skills/websocket-sse/SKILL.md +105 -550
- package/.codex/skills/workflow-engine/SKILL.md +147 -502
- package/.cursor/hooks.json +3 -3
- package/.cursor/skills/add-skill/SKILL.md +79 -32
- package/.cursor/skills/api-development/SKILL.md +83 -377
- package/.cursor/skills/architecture-design/SKILL.md +138 -632
- package/.cursor/skills/backend-annotations/SKILL.md +134 -506
- package/.cursor/skills/banana-image/SKILL.md +10 -3
- package/.cursor/skills/brainstorm/SKILL.md +103 -535
- package/.cursor/skills/bug-detective/SKILL.md +147 -1097
- package/.cursor/skills/bug-detective/references/error-patterns.md +242 -0
- package/.cursor/skills/code-patterns/SKILL.md +116 -426
- package/.cursor/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/.cursor/skills/crud-development/SKILL.md +64 -304
- package/.cursor/skills/data-permission/SKILL.md +105 -412
- package/.cursor/skills/data-permission/references/custom-data-scope.md +90 -0
- package/.cursor/skills/file-oss-management/SKILL.md +106 -714
- package/.cursor/skills/file-oss-management/references/entities.md +105 -0
- package/.cursor/skills/file-oss-management/references/service-impl.md +104 -0
- package/.cursor/skills/git-workflow/SKILL.md +27 -5
- package/.cursor/skills/leniu-api-development/SKILL.md +142 -626
- package/.cursor/skills/leniu-api-development/references/real-examples.md +273 -0
- package/.cursor/skills/leniu-architecture-design/SKILL.md +176 -391
- package/.cursor/skills/leniu-backend-annotations/SKILL.md +132 -519
- package/.cursor/skills/leniu-brainstorm/SKILL.md +132 -541
- package/.cursor/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/.cursor/skills/leniu-crud-development/SKILL.md +232 -938
- package/.cursor/skills/leniu-crud-development/references/templates.md +597 -0
- package/.cursor/skills/leniu-customization-location/SKILL.md +410 -0
- package/.cursor/skills/leniu-data-permission/SKILL.md +70 -0
- package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
- package/.cursor/skills/leniu-java-entity/SKILL.md +76 -590
- package/.cursor/skills/leniu-java-entity/references/templates.md +237 -0
- package/.cursor/skills/leniu-java-export/SKILL.md +94 -379
- package/.cursor/skills/leniu-java-logging/SKILL.md +106 -709
- package/.cursor/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/.cursor/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/.cursor/skills/leniu-java-mybatis/SKILL.md +73 -446
- package/.cursor/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/.cursor/skills/leniu-report-customization/SKILL.md +111 -365
- package/.cursor/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/.cursor/skills/leniu-report-standard-customization/SKILL.md +111 -334
- package/.cursor/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/.cursor/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/.cursor/skills/leniu-security-guard/SKILL.md +133 -347
- package/.cursor/skills/mysql-debug/SKILL.md +364 -0
- package/.cursor/skills/openspec-apply-change/SKILL.md +10 -1
- package/.cursor/skills/openspec-archive-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-continue-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-explore/SKILL.md +10 -1
- package/.cursor/skills/openspec-ff-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-new-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-onboard/SKILL.md +15 -130
- package/.cursor/skills/openspec-sync-specs/SKILL.md +9 -1
- package/.cursor/skills/openspec-verify-change/SKILL.md +9 -1
- package/.cursor/skills/performance-doctor/SKILL.md +110 -434
- package/.cursor/skills/redis-cache/SKILL.md +89 -595
- package/.cursor/skills/redis-cache/references/listeners.md +23 -0
- package/.cursor/skills/scheduled-jobs/SKILL.md +88 -407
- package/.cursor/skills/security-guard/SKILL.md +137 -532
- package/.cursor/skills/security-guard/references/encrypt-config.md +103 -0
- package/.cursor/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/.cursor/skills/sms-mail/SKILL.md +116 -574
- package/.cursor/skills/sms-mail/references/mail-config.md +88 -0
- package/.cursor/skills/sms-mail/references/sms-config.md +74 -0
- package/.cursor/skills/social-login/SKILL.md +112 -514
- package/.cursor/skills/social-login/references/provider-configs.md +118 -0
- package/.cursor/skills/tenant-management/SKILL.md +129 -444
- package/.cursor/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/.cursor/skills/test-development/SKILL.md +86 -540
- package/.cursor/skills/test-development/references/parameterized-examples.md +119 -0
- package/.cursor/skills/utils-toolkit/SKILL.md +52 -305
- package/.cursor/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/.cursor/skills/websocket-sse/SKILL.md +105 -550
- package/.cursor/skills/workflow-engine/SKILL.md +147 -502
- package/package.json +1 -1
|
@@ -16,649 +16,287 @@ description: |
|
|
|
16
16
|
|
|
17
17
|
# 后端高级注解指南
|
|
18
18
|
|
|
19
|
-
>
|
|
20
|
-
> 本文档规范基于框架内置的高级注解功能。所有注解均依赖 Redis 或数据库拦截机制。
|
|
19
|
+
> RuoYi-Vue-Plus 三层架构,包名 `org.dromara.*`
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## 注解总览
|
|
23
22
|
|
|
24
|
-
| 注解 |
|
|
25
|
-
|
|
26
|
-
| `@RateLimiter` | AOP
|
|
27
|
-
| `@RepeatSubmit` | AOP
|
|
28
|
-
| `@Sensitive` | Jackson 序列化器 | VO 字段 | 无 |
|
|
29
|
-
| `@EncryptField` | MyBatis 拦截器 | Entity 字段 | 无 |
|
|
30
|
-
| `@ApiEncrypt` | 拦截器 | Controller 方法 | 无 |
|
|
31
|
-
| `@DataPermission` | AOP
|
|
23
|
+
| 注解 | 机制 | 作用层 | 依赖 |
|
|
24
|
+
|------|------|--------|------|
|
|
25
|
+
| `@RateLimiter` | AOP | Controller 方法 | Redis |
|
|
26
|
+
| `@RepeatSubmit` | AOP | Controller 方法 | Redis + Token |
|
|
27
|
+
| `@Sensitive` | Jackson 序列化器 | VO 字段 | 无 |
|
|
28
|
+
| `@EncryptField` | MyBatis 拦截器 | Entity 字段 | 无 |
|
|
29
|
+
| `@ApiEncrypt` | 拦截器 | Controller 方法 | 无 |
|
|
30
|
+
| `@DataPermission` | AOP | Mapper 接口 | MyBatis |
|
|
32
31
|
|
|
33
32
|
---
|
|
34
33
|
|
|
35
34
|
## 1. @RateLimiter - 接口限流
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
基于 Redis 和令牌桶算法实现分布式限流,防止接口被恶意刷新或频繁访问。
|
|
39
|
-
|
|
40
|
-
### 限流类型
|
|
41
|
-
|
|
42
|
-
| 类型 | 说明 | 适用场景 |
|
|
43
|
-
|------|------|---------|
|
|
44
|
-
| `LimitType.DEFAULT` | 全局限流,所有请求共享配额 | 查询接口、通用接口 |
|
|
45
|
-
| `LimitType.IP` | IP限流,每个IP独立计算 | 登录、验证码、敏感操作 |
|
|
46
|
-
| `LimitType.CLUSTER` | 集群限流,每个节点独立 | 集群部署、分布式场景 |
|
|
47
|
-
|
|
48
|
-
### 使用示例
|
|
49
|
-
|
|
50
|
-
```java
|
|
51
|
-
import org.dromara.common.ratelimiter.annotation.RateLimiter;
|
|
52
|
-
import org.dromara.common.ratelimiter.enums.LimitType;
|
|
53
|
-
|
|
54
|
-
@RestController
|
|
55
|
-
@RequestMapping("/demo")
|
|
56
|
-
public class DemoController {
|
|
57
|
-
|
|
58
|
-
// ✅ 基本用法:60秒内最多100次
|
|
59
|
-
@RateLimiter(time = 60, count = 100)
|
|
60
|
-
@GetMapping("/list")
|
|
61
|
-
public R<List<XxxVo>> list() { }
|
|
62
|
-
|
|
63
|
-
// ✅ IP限流:每个IP每分钟最多10次
|
|
64
|
-
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
|
|
65
|
-
@PostMapping("/login")
|
|
66
|
-
public R<String> login() { }
|
|
67
|
-
|
|
68
|
-
// ✅ 集群限流:每个节点独立限流
|
|
69
|
-
@RateLimiter(time = 60, count = 20, limitType = LimitType.CLUSTER)
|
|
70
|
-
@GetMapping("/data")
|
|
71
|
-
public R<DataVo> getData() { }
|
|
72
|
-
|
|
73
|
-
// ✅ 动态key:基于用户ID限流
|
|
74
|
-
@RateLimiter(key = "#userId", time = 60, count = 5)
|
|
75
|
-
@PostMapping("/submit")
|
|
76
|
-
public R<Void> submit(Long userId) { }
|
|
77
|
-
|
|
78
|
-
// ✅ 自定义错误消息
|
|
79
|
-
@RateLimiter(time = 60, count = 10, message = "访问过于频繁,请稍后再试")
|
|
80
|
-
@GetMapping("/sensitive")
|
|
81
|
-
public R<DataVo> getSensitiveData() { }
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 注解参数
|
|
36
|
+
**导入**:`org.dromara.common.ratelimiter.annotation.RateLimiter`
|
|
86
37
|
|
|
87
38
|
| 参数 | 类型 | 默认值 | 说明 |
|
|
88
39
|
|------|------|--------|------|
|
|
89
|
-
| `key` | String | "" | 限流key,支持SpEL
|
|
40
|
+
| `key` | String | "" | 限流key,支持SpEL |
|
|
90
41
|
| `time` | int | 60 | 时间窗口(秒) |
|
|
91
42
|
| `count` | int | 100 | 最大请求次数 |
|
|
92
|
-
| `limitType` | LimitType | DEFAULT |
|
|
93
|
-
| `message` | String | 国际化key |
|
|
94
|
-
| `timeout` | int | 86400 | Redis超时(秒) |
|
|
95
|
-
|
|
96
|
-
### 推荐配置
|
|
43
|
+
| `limitType` | LimitType | DEFAULT | DEFAULT/IP/CLUSTER |
|
|
44
|
+
| `message` | String | 国际化key | 错误提示 |
|
|
97
45
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
46
|
+
```java
|
|
47
|
+
// 全局限流:60秒100次
|
|
48
|
+
@RateLimiter(time = 60, count = 100)
|
|
49
|
+
@GetMapping("/list")
|
|
50
|
+
public R<List<XxxVo>> list() { }
|
|
51
|
+
|
|
52
|
+
// IP限流:每个IP每分钟10次(登录、验证码)
|
|
53
|
+
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
|
|
54
|
+
@PostMapping("/login")
|
|
55
|
+
public R<String> login() { }
|
|
56
|
+
|
|
57
|
+
// 动态key:基于用户ID限流
|
|
58
|
+
@RateLimiter(key = "#userId", time = 60, count = 5)
|
|
59
|
+
@PostMapping("/submit")
|
|
60
|
+
public R<Void> submit(Long userId) { }
|
|
61
|
+
```
|
|
105
62
|
|
|
106
63
|
---
|
|
107
64
|
|
|
108
65
|
## 2. @RepeatSubmit - 防重复提交
|
|
109
66
|
|
|
110
|
-
|
|
111
|
-
基于 Redis 分布式锁防止短时间内重复提交表单或请求,保证接口幂等性。
|
|
112
|
-
|
|
113
|
-
### 使用示例
|
|
114
|
-
|
|
115
|
-
```java
|
|
116
|
-
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
|
117
|
-
import java.util.concurrent.TimeUnit;
|
|
118
|
-
|
|
119
|
-
@RestController
|
|
120
|
-
@RequestMapping("/order")
|
|
121
|
-
public class OrderController {
|
|
122
|
-
|
|
123
|
-
// ✅ 默认:5秒内不能重复提交
|
|
124
|
-
@RepeatSubmit()
|
|
125
|
-
@PostMapping()
|
|
126
|
-
public R<Long> addOrder(@RequestBody OrderBo bo) {
|
|
127
|
-
return R.ok(orderService.add(bo));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ✅ 自定义间隔:10秒
|
|
131
|
-
@RepeatSubmit(interval = 10000)
|
|
132
|
-
@PostMapping("/pay")
|
|
133
|
-
public R<Void> pay(@RequestBody PayBo bo) { }
|
|
134
|
-
|
|
135
|
-
// ✅ 使用秒作为单位
|
|
136
|
-
@RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS)
|
|
137
|
-
@PostMapping("/submit")
|
|
138
|
-
public R<Void> submit(@RequestBody SubmitBo bo) { }
|
|
139
|
-
|
|
140
|
-
// ✅ 自定义提示消息
|
|
141
|
-
@RepeatSubmit(interval = 5000, message = "请勿重复提交订单")
|
|
142
|
-
@PostMapping("/create")
|
|
143
|
-
public R<Long> createOrder(@RequestBody OrderBo bo) { }
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### 注解参数
|
|
67
|
+
**导入**:`org.dromara.common.idempotent.annotation.RepeatSubmit`
|
|
148
68
|
|
|
149
69
|
| 参数 | 类型 | 默认值 | 说明 |
|
|
150
70
|
|------|------|--------|------|
|
|
151
71
|
| `interval` | int | 5000 | 间隔时间(毫秒) |
|
|
152
72
|
| `timeUnit` | TimeUnit | MILLISECONDS | 时间单位 |
|
|
153
|
-
| `message` | String | 国际化key |
|
|
154
|
-
|
|
155
|
-
### 工作原理
|
|
156
|
-
|
|
157
|
-
1. **提交前** - 生成请求唯一键:`MD5(token + ":" + 序列化参数)`
|
|
158
|
-
2. **Redis检查** - 尝试在 Redis 中设置该键,有效期为 `interval`
|
|
159
|
-
3. **重复检测** - 如果已存在该键,说明重复提交,直接返回错误
|
|
160
|
-
4. **成功处理** - 如果返回 `R.SUCCESS`,保持 Redis 数据不删除(防止短时间重复)
|
|
161
|
-
5. **异常处理** - 如果异常,删除 Redis 数据允许重新提交
|
|
73
|
+
| `message` | String | 国际化key | 错误提示 |
|
|
162
74
|
|
|
163
|
-
|
|
75
|
+
```java
|
|
76
|
+
// 默认5秒
|
|
77
|
+
@RepeatSubmit()
|
|
78
|
+
@PostMapping()
|
|
79
|
+
public R<Long> add(@RequestBody OrderBo bo) { }
|
|
80
|
+
|
|
81
|
+
// 10秒间隔 + 自定义提示
|
|
82
|
+
@RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS, message = "请勿重复提交")
|
|
83
|
+
@PostMapping("/pay")
|
|
84
|
+
public R<Void> pay(@RequestBody PayBo bo) { }
|
|
85
|
+
```
|
|
164
86
|
|
|
165
|
-
|
|
166
|
-
|------|---------|
|
|
167
|
-
| 普通表单 | 3-5秒 |
|
|
168
|
-
| 订单创建 | 10秒 |
|
|
169
|
-
| 支付操作 | 30秒 |
|
|
170
|
-
| 文件上传 | 10秒 |
|
|
87
|
+
**原理**:`MD5(token + ":" + 序列化参数)` -> Redis 设置/检查 -> 成功保留key防重复 -> 异常删除key允许重试。
|
|
171
88
|
|
|
172
89
|
---
|
|
173
90
|
|
|
174
91
|
## 3. @Sensitive - 数据脱敏
|
|
175
92
|
|
|
176
|
-
|
|
177
|
-
在 JSON 序列化时自动对敏感字段进行脱敏处理,支持基于角色/权限的访问控制。
|
|
93
|
+
**导入**:`org.dromara.common.sensitive.annotation.Sensitive`
|
|
178
94
|
|
|
179
95
|
### 脱敏策略
|
|
180
96
|
|
|
181
|
-
| 策略 |
|
|
182
|
-
|
|
183
|
-
| `ID_CARD` |
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `ADDRESS` | 地址 | 北京市朝阳区某街道 → 北京市朝阳区**** |
|
|
189
|
-
| `FIXED_PHONE` | 固定电话 | 010-12345678 → 010****5678 |
|
|
190
|
-
| `PASSWORD` | 密码 | ****** |
|
|
191
|
-
| `IPV4` | IPv4地址 | 192.168.1.1 → 192.*.*.* |
|
|
192
|
-
| `IPV6` | IPv6地址 | 2001:0db8:... → 2001:*:*:*:*:*:*:* |
|
|
193
|
-
| `USER_ID` | 用户ID | 0 |
|
|
194
|
-
| `CAR_LICENSE` | 车牌号 | 京A12345 → 京A1***5 |
|
|
195
|
-
| `FIRST_MASK` | 只显示第一个字符 | abcdef → a***** |
|
|
196
|
-
| `STRING_MASK` | 通用字符串脱敏 | abcdefghij → abcd****ghij |
|
|
197
|
-
| `MASK_HIGH_SECURITY` | 高安全级别脱敏 | token → to******en |
|
|
198
|
-
| `CLEAR` | 清空为空字符串 | abc → "" |
|
|
199
|
-
| `CLEAR_TO_NULL` | 清空为null | abc → null |
|
|
200
|
-
|
|
201
|
-
### 使用示例
|
|
97
|
+
| 策略 | 效果 | 策略 | 效果 |
|
|
98
|
+
|------|------|------|------|
|
|
99
|
+
| `ID_CARD` | 110\*\*\*5431 | `PHONE` | 176\*\*\*\*5371 |
|
|
100
|
+
| `EMAIL` | t\*\*@example.com | `BANK_CARD` | 6222\*\*\*2853 |
|
|
101
|
+
| `CHINESE_NAME` | 张\* | `ADDRESS` | 北京市朝阳区\*\*\*\* |
|
|
102
|
+
| `PASSWORD` | \*\*\*\*\*\* | `IPV4` | 192.\*.\*.\* |
|
|
103
|
+
| `FIRST_MASK` | a\*\*\*\*\* | `CLEAR` | "" |
|
|
202
104
|
|
|
203
105
|
```java
|
|
204
|
-
import org.dromara.common.sensitive.annotation.Sensitive;
|
|
205
|
-
import org.dromara.common.sensitive.core.SensitiveStrategy;
|
|
206
|
-
|
|
207
106
|
public class UserVo {
|
|
208
|
-
|
|
209
|
-
private Long id;
|
|
210
|
-
private String name;
|
|
211
|
-
|
|
212
|
-
// ✅ 手机号脱敏(所有人看脱敏数据)
|
|
107
|
+
// 所有人看脱敏数据
|
|
213
108
|
@Sensitive(strategy = SensitiveStrategy.PHONE)
|
|
214
109
|
private String phone;
|
|
215
110
|
|
|
216
|
-
//
|
|
111
|
+
// admin 角色可查看原数据
|
|
217
112
|
@Sensitive(strategy = SensitiveStrategy.ID_CARD, roleKey = {"admin"})
|
|
218
113
|
private String idCard;
|
|
219
114
|
|
|
220
|
-
//
|
|
115
|
+
// 需要权限才能看原数据
|
|
221
116
|
@Sensitive(strategy = SensitiveStrategy.EMAIL, perms = {"system:user:detail"})
|
|
222
117
|
private String email;
|
|
223
|
-
|
|
224
|
-
// ✅ 银行卡脱敏,admin角色或有权限都可查看
|
|
225
|
-
@Sensitive(strategy = SensitiveStrategy.BANK_CARD,
|
|
226
|
-
roleKey = {"admin"},
|
|
227
|
-
perms = {"finance:account:query"})
|
|
228
|
-
private String bankCard;
|
|
229
118
|
}
|
|
230
119
|
```
|
|
231
120
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
235
|
-
|------|------|--------|------|
|
|
236
|
-
| `strategy` | SensitiveStrategy | 必填 | 脱敏策略 |
|
|
237
|
-
| `roleKey` | String[] | {} | 可查看原数据的角色 |
|
|
238
|
-
| `perms` | String[] | {} | 可查看原数据的权限 |
|
|
239
|
-
|
|
240
|
-
**权限控制逻辑**:
|
|
241
|
-
- `roleKey` 和 `perms` 都为空:所有人都看脱敏数据
|
|
242
|
-
- 满足任一 `roleKey` **或** 任一 `perms`:可查看原数据
|
|
243
|
-
- 两者是 **OR** 关系,不是 AND
|
|
121
|
+
**权限逻辑**:roleKey 和 perms 都为空 = 全部脱敏;满足任一 roleKey **或** 任一 perms = 可查看原数据(OR 关系)。
|
|
244
122
|
|
|
245
123
|
---
|
|
246
124
|
|
|
247
125
|
## 4. @EncryptField - 字段加密
|
|
248
126
|
|
|
249
|
-
|
|
250
|
-
在数据库级别对敏感字段进行加密存储,读取时自动解密。支持多种加密算法。
|
|
251
|
-
|
|
252
|
-
### 支持的算法
|
|
127
|
+
**导入**:`org.dromara.common.encrypt.annotation.EncryptField`
|
|
253
128
|
|
|
254
|
-
| 算法 | 说明 |
|
|
255
|
-
|
|
256
|
-
| `BASE64` |
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
259
|
-
| `SM2` | SM2非对称加密 | 低 | 高 | 国密要求 |
|
|
260
|
-
| `SM4` | SM4对称加密 | 中 | 高 | 国密要求 |
|
|
261
|
-
|
|
262
|
-
### 使用示例
|
|
129
|
+
| 算法 | 说明 | 算法 | 说明 |
|
|
130
|
+
|------|------|------|------|
|
|
131
|
+
| `BASE64` | 编码(低安全) | `AES` | 对称加密(推荐) |
|
|
132
|
+
| `RSA` | 非对称(高安全) | `SM2` | 国密非对称 |
|
|
133
|
+
| `SM4` | 国密对称 | | |
|
|
263
134
|
|
|
264
135
|
```java
|
|
265
|
-
import org.dromara.common.encrypt.annotation.EncryptField;
|
|
266
|
-
import org.dromara.common.encrypt.enums.AlgorithmType;
|
|
267
|
-
|
|
268
136
|
@Data
|
|
269
137
|
@TableName("test_demo")
|
|
270
138
|
public class TestDemo extends BaseEntity {
|
|
271
|
-
|
|
272
|
-
private Long id;
|
|
273
|
-
|
|
274
|
-
// ✅ AES加密(使用yml配置的秘钥)
|
|
139
|
+
// AES 加密(使用 yml 全局秘钥)
|
|
275
140
|
@EncryptField(algorithm = AlgorithmType.AES)
|
|
276
|
-
private String
|
|
141
|
+
private String phone;
|
|
277
142
|
|
|
278
|
-
//
|
|
143
|
+
// RSA 加密(指定公私钥)
|
|
279
144
|
@EncryptField(algorithm = AlgorithmType.RSA,
|
|
280
|
-
privateKey = "
|
|
281
|
-
|
|
282
|
-
private String rsaField;
|
|
145
|
+
privateKey = "MIICdQ...", publicKey = "MFkwEw...")
|
|
146
|
+
private String idCard;
|
|
283
147
|
|
|
284
|
-
//
|
|
148
|
+
// 使用 yml 默认配置
|
|
285
149
|
@EncryptField
|
|
286
|
-
private String
|
|
287
|
-
|
|
288
|
-
// ❌ 不加密字段 - 无需添加注解
|
|
289
|
-
private String normalField;
|
|
150
|
+
private String secretField;
|
|
290
151
|
}
|
|
291
152
|
```
|
|
292
153
|
|
|
293
|
-
|
|
154
|
+
**配置**:
|
|
294
155
|
|
|
295
156
|
```yaml
|
|
296
157
|
mybatis-encryptor:
|
|
297
|
-
# 是否开启加密
|
|
298
158
|
enable: false
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
#
|
|
304
|
-
password: your-secret-key-16-chars
|
|
305
|
-
# RSA/SM2 公私钥
|
|
306
|
-
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/...
|
|
307
|
-
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6...
|
|
159
|
+
algorithm: BASE64 # 默认算法
|
|
160
|
+
encode: BASE64 # 编码方式:BASE64 | HEX
|
|
161
|
+
password: your-secret-16 # AES/SM4 秘钥(16字符)
|
|
162
|
+
publicKey: MFwwDQ... # RSA/SM2 公钥
|
|
163
|
+
privateKey: MIIBVAIBADANBg... # RSA/SM2 私钥
|
|
308
164
|
```
|
|
309
165
|
|
|
310
|
-
### 注解参数
|
|
311
|
-
|
|
312
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
313
|
-
|------|------|--------|------|
|
|
314
|
-
| `algorithm` | AlgorithmType | DEFAULT | 加密算法 |
|
|
315
|
-
| `password` | String | "" | 对称算法秘钥 |
|
|
316
|
-
| `publicKey` | String | "" | 非对称算法公钥 |
|
|
317
|
-
| `privateKey` | String | "" | 非对称算法私钥 |
|
|
318
|
-
| `encode` | EncodeType | DEFAULT | 编码方式 |
|
|
319
|
-
|
|
320
166
|
---
|
|
321
167
|
|
|
322
168
|
## 5. @ApiEncrypt - 接口加密
|
|
323
169
|
|
|
324
|
-
|
|
325
|
-
对接口的请求和响应进行加密处理,确保数据在网络传输中的安全性。
|
|
326
|
-
|
|
327
|
-
### 使用示例
|
|
170
|
+
**导入**:`org.dromara.common.encrypt.annotation.ApiEncrypt`
|
|
328
171
|
|
|
329
172
|
```java
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
@
|
|
333
|
-
@
|
|
334
|
-
public class UserController {
|
|
335
|
-
|
|
336
|
-
// ✅ 加密响应数据
|
|
337
|
-
@ApiEncrypt(response = true)
|
|
338
|
-
@GetMapping("/{id}")
|
|
339
|
-
public R<UserVo> getUser(@PathVariable Long id) {
|
|
340
|
-
// 返回的数据会被自动加密
|
|
341
|
-
return R.ok(userService.getById(id));
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// ✅ 不加密响应(默认)
|
|
345
|
-
@GetMapping("/public")
|
|
346
|
-
public R<List<PublicVo>> getPublicData() { }
|
|
347
|
-
}
|
|
173
|
+
// 加密响应数据
|
|
174
|
+
@ApiEncrypt(response = true)
|
|
175
|
+
@GetMapping("/{id}")
|
|
176
|
+
public R<UserVo> getUser(@PathVariable Long id) { }
|
|
348
177
|
```
|
|
349
178
|
|
|
350
|
-
|
|
179
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
180
|
+
|------|------|--------|------|
|
|
181
|
+
| `response` | boolean | false | 是否加密响应 |
|
|
182
|
+
|
|
183
|
+
**配置**:
|
|
351
184
|
|
|
352
185
|
```yaml
|
|
353
186
|
api-decrypt:
|
|
354
|
-
# 是否开启接口加密
|
|
355
187
|
enabled: true
|
|
356
|
-
# 加密头标识
|
|
357
188
|
headerFlag: encrypt-key
|
|
358
|
-
#
|
|
359
|
-
|
|
360
|
-
# 请求解密私钥(服务端使用私钥解密客户端加密的数据)
|
|
361
|
-
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYouqNxaY7...
|
|
189
|
+
publicKey: MFwwDQ... # 响应加密公钥
|
|
190
|
+
privateKey: MIIBVAIBADANBg... # 请求解密私钥
|
|
362
191
|
```
|
|
363
192
|
|
|
364
|
-
### 注解参数
|
|
365
|
-
|
|
366
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
367
|
-
|------|------|--------|------|
|
|
368
|
-
| `response` | boolean | false | 是否加密响应 |
|
|
369
|
-
|
|
370
193
|
---
|
|
371
194
|
|
|
372
195
|
## 6. @DataPermission - 数据权限
|
|
373
196
|
|
|
374
|
-
|
|
375
|
-
在 SQL 查询时自动拼接数据权限过滤条件,实现部门级或用户级数据隔离(行级权限)。
|
|
376
|
-
|
|
377
|
-
### 使用示例
|
|
197
|
+
**导入**:`org.dromara.common.mybatis.annotation.DataPermission`、`@DataColumn`
|
|
378
198
|
|
|
379
199
|
```java
|
|
380
|
-
import org.dromara.common.mybatis.annotation.DataPermission;
|
|
381
|
-
import org.dromara.common.mybatis.annotation.DataColumn;
|
|
382
|
-
|
|
383
200
|
public interface OrderMapper extends BaseMapperPlus<Order, OrderVo> {
|
|
384
201
|
|
|
385
|
-
//
|
|
202
|
+
// 按部门+创建人隔离
|
|
386
203
|
@DataPermission({
|
|
387
204
|
@DataColumn(key = "deptName", value = "dept_id"),
|
|
388
205
|
@DataColumn(key = "userName", value = "create_by")
|
|
389
206
|
})
|
|
390
|
-
default Page<OrderVo> selectPageOrderList(Page<Order> page, Wrapper<Order>
|
|
391
|
-
return this.selectVoPage(page,
|
|
207
|
+
default Page<OrderVo> selectPageOrderList(Page<Order> page, Wrapper<Order> wrapper) {
|
|
208
|
+
return this.selectVoPage(page, wrapper);
|
|
392
209
|
}
|
|
393
210
|
|
|
394
|
-
//
|
|
395
|
-
@DataPermission({
|
|
396
|
-
@DataColumn(key = "deptName", value = "create_dept")
|
|
397
|
-
})
|
|
398
|
-
List<OrderVo> selectByDept(Long deptId);
|
|
399
|
-
|
|
400
|
-
// ✅ 指定权限标识:拥有此权限的角色不拼接过滤条件
|
|
401
|
-
@DataPermission({
|
|
402
|
-
@DataColumn(key = "deptName", value = "dept_id", permission = "order:all")
|
|
403
|
-
})
|
|
404
|
-
List<OrderVo> selectAllOrders();
|
|
405
|
-
|
|
406
|
-
// ✅ 使用表别名
|
|
211
|
+
// 使用表别名
|
|
407
212
|
@DataPermission({
|
|
408
213
|
@DataColumn(key = "deptName", value = "d.dept_id"),
|
|
409
214
|
@DataColumn(key = "userName", value = "u.create_by")
|
|
410
215
|
})
|
|
411
|
-
List<OrderVo> selectWithJoin(@Param(Constants.WRAPPER) Wrapper<Order>
|
|
216
|
+
List<OrderVo> selectWithJoin(@Param(Constants.WRAPPER) Wrapper<Order> wrapper);
|
|
412
217
|
}
|
|
413
218
|
```
|
|
414
219
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
**@DataPermission**
|
|
220
|
+
**@DataColumn 参数**:
|
|
418
221
|
|
|
419
|
-
| 参数 |
|
|
420
|
-
|
|
421
|
-
| `
|
|
422
|
-
| `
|
|
222
|
+
| 参数 | 默认值 | 说明 |
|
|
223
|
+
|------|--------|------|
|
|
224
|
+
| `key` | "deptName" | 占位符关键字 |
|
|
225
|
+
| `value` | "dept_id" | 替换的字段名 |
|
|
226
|
+
| `permission` | "" | 拥有此权限则不过滤 |
|
|
423
227
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
427
|
-
|------|------|--------|------|
|
|
428
|
-
| `key` | String[] | "deptName" | 占位符关键字 |
|
|
429
|
-
| `value` | String[] | "dept_id" | 替换的字段名 |
|
|
430
|
-
| `permission` | String | "" | 权限标识(拥有此权限不过滤) |
|
|
431
|
-
|
|
432
|
-
### 数据权限范围
|
|
433
|
-
|
|
434
|
-
系统支持的权限范围:
|
|
435
|
-
1. **全部数据** - 无过滤条件
|
|
436
|
-
2. **本部门数据** - 仅查看本部门数据
|
|
437
|
-
3. **本部门及以下** - 本部门 + 所有子部门数据
|
|
438
|
-
4. **仅本人数据** - 只看自己创建的数据
|
|
439
|
-
5. **自定义权限** - 根据指定权限控制
|
|
228
|
+
**权限范围**:全部数据 / 本部门 / 本部门及以下 / 仅本人 / 自定义。
|
|
440
229
|
|
|
441
230
|
---
|
|
442
231
|
|
|
443
|
-
##
|
|
444
|
-
|
|
445
|
-
### 1. 限流 + 防重复提交组合
|
|
232
|
+
## 七、组合使用
|
|
446
233
|
|
|
447
234
|
```java
|
|
448
|
-
|
|
449
|
-
@
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
@RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS)
|
|
455
|
-
@Log(title = "订单", businessType = BusinessType.INSERT)
|
|
456
|
-
@PostMapping()
|
|
457
|
-
public R<Long> addOrder(@Validated @RequestBody OrderBo bo) {
|
|
458
|
-
return R.ok(orderService.add(bo));
|
|
459
|
-
}
|
|
460
|
-
}
|
|
235
|
+
// 接口防护:限流 + 防重复 + 日志
|
|
236
|
+
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
|
|
237
|
+
@RepeatSubmit(interval = 10, timeUnit = TimeUnit.SECONDS)
|
|
238
|
+
@Log(title = "订单", businessType = BusinessType.INSERT)
|
|
239
|
+
@PostMapping()
|
|
240
|
+
public R<Long> addOrder(@Validated @RequestBody OrderBo bo) { }
|
|
461
241
|
```
|
|
462
242
|
|
|
463
|
-
### 2. VO层脱敏示例
|
|
464
|
-
|
|
465
|
-
```java
|
|
466
|
-
public class UserDetailVo {
|
|
467
|
-
|
|
468
|
-
private Long id;
|
|
469
|
-
|
|
470
|
-
// 脱敏:手机号
|
|
471
|
-
@Sensitive(strategy = SensitiveStrategy.PHONE)
|
|
472
|
-
private String phone;
|
|
473
|
-
|
|
474
|
-
// 脱敏:身份证(admin角色可查看)
|
|
475
|
-
@Sensitive(strategy = SensitiveStrategy.ID_CARD, roleKey = {"admin"})
|
|
476
|
-
private String idCard;
|
|
477
|
-
|
|
478
|
-
// 脱敏:邮箱(需要权限才能查看原数据)
|
|
479
|
-
@Sensitive(strategy = SensitiveStrategy.EMAIL, perms = {"system:user:detail"})
|
|
480
|
-
private String email;
|
|
481
|
-
|
|
482
|
-
// 脱敏:银行卡
|
|
483
|
-
@Sensitive(strategy = SensitiveStrategy.BANK_CARD)
|
|
484
|
-
private String bankCard;
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### 3. Entity层字段加密
|
|
489
|
-
|
|
490
243
|
```java
|
|
491
|
-
|
|
244
|
+
// 数据保护分层:Entity 加密存储 + VO 脱敏展示
|
|
492
245
|
@TableName("sys_user")
|
|
493
246
|
public class SysUser extends TenantEntity {
|
|
494
|
-
|
|
495
|
-
private Long id;
|
|
496
|
-
private String username;
|
|
497
|
-
|
|
498
|
-
// ✅ 加密存储手机号
|
|
499
247
|
@EncryptField(algorithm = AlgorithmType.AES)
|
|
500
|
-
private String phone;
|
|
248
|
+
private String phone; // 存储时加密
|
|
249
|
+
}
|
|
501
250
|
|
|
502
|
-
|
|
503
|
-
@
|
|
504
|
-
private String
|
|
251
|
+
public class UserVo {
|
|
252
|
+
@Sensitive(strategy = SensitiveStrategy.PHONE)
|
|
253
|
+
private String phone; // 序列化时脱敏
|
|
505
254
|
}
|
|
506
255
|
```
|
|
507
256
|
|
|
508
257
|
---
|
|
509
258
|
|
|
510
|
-
##
|
|
511
|
-
|
|
512
|
-
### ❌ 不要做
|
|
259
|
+
## 八、常见错误
|
|
513
260
|
|
|
514
261
|
```java
|
|
515
|
-
//
|
|
262
|
+
// ❌ @RateLimiter 用在 Service 方法上(无效,只能 Controller)
|
|
516
263
|
@Service
|
|
517
264
|
public class XxxService {
|
|
518
|
-
@RateLimiter(...) //
|
|
265
|
+
@RateLimiter(...) // 无效!
|
|
519
266
|
public void doSomething() { }
|
|
520
267
|
}
|
|
521
268
|
|
|
522
|
-
//
|
|
523
|
-
@DataPermission //
|
|
269
|
+
// ❌ @DataPermission 未配置 @DataColumn
|
|
270
|
+
@DataPermission // 空注解无效!
|
|
524
271
|
public interface UserMapper { }
|
|
525
272
|
|
|
526
|
-
//
|
|
527
|
-
// @
|
|
528
|
-
// @EncryptField 是数据库加密(Entity层)
|
|
529
|
-
// 不应该在 Entity 中使用 @Sensitive
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
### ✅ 正确做法
|
|
273
|
+
// ❌ @Sensitive 用在 Entity 上(应在 VO 上)
|
|
274
|
+
// ❌ @EncryptField 用在 VO 上(应在 Entity 上)
|
|
533
275
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
@RestController
|
|
537
|
-
public class XxxController {
|
|
538
|
-
@RateLimiter(...) // ✅
|
|
539
|
-
@GetMapping("/test")
|
|
540
|
-
public R<Void> test() { }
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// 正确2:@DataPermission 用在 Mapper 接口或方法上(支持类/方法两级)
|
|
544
|
-
@DataPermission({ // ✅ 类级别:所有方法默认使用此配置
|
|
545
|
-
@DataColumn(key = "deptName", value = "dept_id")
|
|
546
|
-
})
|
|
276
|
+
// ✅ @DataPermission 支持类级别 + 方法级别(方法覆盖类)
|
|
277
|
+
@DataPermission({ @DataColumn(key = "deptName", value = "dept_id") })
|
|
547
278
|
public interface UserMapper {
|
|
548
|
-
@DataPermission({
|
|
549
|
-
@DataColumn(key = "deptName", value = "dept_id"),
|
|
550
|
-
@DataColumn(key = "userName", value = "create_by")
|
|
551
|
-
})
|
|
279
|
+
@DataPermission({ /* 方法级覆盖 */ })
|
|
552
280
|
List<UserVo> selectList();
|
|
553
281
|
}
|
|
554
|
-
|
|
555
|
-
// 正确3:分层使用注解
|
|
556
|
-
@Entity
|
|
557
|
-
public class SysUser {
|
|
558
|
-
@EncryptField(algorithm = AlgorithmType.AES) // 存储时加密
|
|
559
|
-
private String phone;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
@VO
|
|
563
|
-
public class UserVo {
|
|
564
|
-
@Sensitive(strategy = SensitiveStrategy.PHONE) // 序列化时脱敏
|
|
565
|
-
private String phone;
|
|
566
|
-
}
|
|
567
282
|
```
|
|
568
283
|
|
|
569
284
|
---
|
|
570
285
|
|
|
571
|
-
##
|
|
572
|
-
|
|
573
|
-
使用后端注解前必须检查:
|
|
286
|
+
## 九、参考实现
|
|
574
287
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
- [ ] **是否正确处理了异常**?脱敏/加密异常不应该导致请求失败
|
|
582
|
-
- [ ] **是否测试了权限控制**?不同角色应该看到不同数据
|
|
583
|
-
- [ ] **依赖是否齐全**?Redis、MyBatis-Plus 等必需库
|
|
584
|
-
- [ ] **导入路径是否正确**?使用 `org.dromara.*` 包名
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
## 快速参考
|
|
589
|
-
|
|
590
|
-
### 注解速查表
|
|
591
|
-
|
|
592
|
-
```
|
|
593
|
-
@RateLimiter → 接口限流(Controller 方法)
|
|
594
|
-
@RepeatSubmit → 防重复提交(POST/PUT 方法)
|
|
595
|
-
@Sensitive → 数据脱敏(VO 字段)
|
|
596
|
-
@EncryptField → 字段加密(Entity 字段)
|
|
597
|
-
@ApiEncrypt → 接口加密(Controller 方法)
|
|
598
|
-
@DataPermission → 数据权限(Mapper 接口)
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
### 组合使用速查
|
|
602
|
-
|
|
603
|
-
```
|
|
604
|
-
接口防护: @RateLimiter + @RepeatSubmit
|
|
605
|
-
数据保护: @EncryptField + @Sensitive + @ApiEncrypt
|
|
606
|
-
数据隔离: @DataPermission(Mapper)
|
|
607
|
-
```
|
|
288
|
+
| 类型 | 位置 |
|
|
289
|
+
|------|------|
|
|
290
|
+
| 限流示例 | `org.dromara.demo.controller.RedisRateLimiterController` |
|
|
291
|
+
| 脱敏示例 | `org.dromara.demo.controller.TestSensitiveController` |
|
|
292
|
+
| 加密示例 | `org.dromara.demo.domain.TestDemo` |
|
|
293
|
+
| 数据权限 | `org.dromara.system.mapper.SysUserMapper` |
|
|
608
294
|
|
|
609
|
-
|
|
295
|
+
**配置位置**:
|
|
610
296
|
|
|
611
297
|
```
|
|
612
298
|
application.yml:
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
---
|
|
619
|
-
|
|
620
|
-
## 参考实现
|
|
621
|
-
|
|
622
|
-
查看框架中的完整实现:
|
|
623
|
-
|
|
624
|
-
- **限流示例**: `org.dromara.demo.controller.RedisRateLimiterController`(@RateLimiter 详细用法)
|
|
625
|
-
- **脱敏示例**: `org.dromara.demo.controller.TestSensitiveController`(@Sensitive 各策略)
|
|
626
|
-
- **防重示例**: 任何 POST/PUT 方法都有 @RepeatSubmit
|
|
627
|
-
- **加密示例**: `org.dromara.demo.domain.TestDemo`(@EncryptField 使用)
|
|
628
|
-
- **数据权限**: `org.dromara.system.mapper.SysUserMapper`(@DataPermission 使用)
|
|
629
|
-
- **对象映射**: 系统 BO/VO 类中的 `@AutoMapper` 注解(见 crud-development 技能)
|
|
630
|
-
|
|
631
|
-
**特别注意**:上述参考代码是本项目的标准实现,严格遵循三层架构和后端规范。
|
|
632
|
-
|
|
633
|
-
---
|
|
634
|
-
|
|
635
|
-
## 关键配置
|
|
636
|
-
|
|
637
|
-
```yaml
|
|
638
|
-
# Redis 配置(必须,限流和防重复依赖)
|
|
639
|
-
# ⚠️ Spring Boot 3 使用 spring.data.redis(非 spring.redis)
|
|
640
|
-
# 本项目使用 Redisson 客户端(非 Jedis/Lettuce),连接池由 Redisson 管理
|
|
641
|
-
spring:
|
|
642
|
-
data:
|
|
643
|
-
redis:
|
|
644
|
-
host: localhost
|
|
645
|
-
port: 6379
|
|
646
|
-
password:
|
|
647
|
-
timeout: 10s
|
|
648
|
-
|
|
649
|
-
# 字段加密配置
|
|
650
|
-
mybatis-encryptor:
|
|
651
|
-
enable: false
|
|
652
|
-
algorithm: BASE64
|
|
653
|
-
encode: BASE64
|
|
654
|
-
password: your-secret-key
|
|
655
|
-
publicKey: your-public-key
|
|
656
|
-
privateKey: your-private-key
|
|
657
|
-
|
|
658
|
-
# 接口加密配置
|
|
659
|
-
api-decrypt:
|
|
660
|
-
enabled: true
|
|
661
|
-
headerFlag: encrypt-key
|
|
662
|
-
publicKey: your-public-key
|
|
663
|
-
privateKey: your-private-key
|
|
299
|
+
mybatis-encryptor.* → @EncryptField 全局配置
|
|
300
|
+
api-decrypt.* → @ApiEncrypt 全局配置
|
|
301
|
+
spring.data.redis.* → @RateLimiter/@RepeatSubmit 依赖
|
|
664
302
|
```
|