知识 - Encrypt
# 加解密基础
本内容将介绍接口数据加解密以及数据库字段值加解密。
加密主要利用了 hutool 提供的加密方法,其中有对称加密和非对称加密两种。
依赖:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.76</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.6</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 枚举
加解密支持的算法枚举类
@Getter
@AllArgsConstructor
public enum AlgorithmType {
/**
* 默认走yml配置
*/
DEFAULT(null),
/**
* base64
*/
BASE64(Base64Encryptor.class),
/**
* aes
*/
AES(AesEncryptor.class),
/**
* rsa
*/
RSA(RsaEncryptor.class),
/**
* sm2
*/
SM2(Sm2Encryptor.class),
/**
* sm4
*/
SM4(Sm4Encryptor.class);
private final Class<? extends AbstractEncryptor> clazz;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 加解密算法
先提供 Encryptor
接口。
public interface Encryptor {
/**
* 获得当前算法
*/
AlgorithmType algorithm();
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
* @return 加密后的字符串
*/
String encrypt(String value, EncodeType encodeType);
/**
* 解密
*
* @param value 待加密字符串
* @return 解密后的字符串
*/
String decrypt(String value);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 基础实现类
public abstract class AbstractEncryptor implements Encryptor {
public AbstractEncryptor(EncryptContext context) {
// 用户配置校验与配置注入
}
}
2
3
4
5
6
# Base64 加解密类
public class Base64Encryptor extends AbstractEncryptor {
public Base64Encryptor(EncryptContext context) {
super(context);
}
@Override
public AlgorithmType algorithm() {
return AlgorithmType.BASE64;
}
@Override
public String encrypt(String value, EncodeType encodeType) {
return EncryptHelper.encryptByBase64(value);
}
@Override
public String decrypt(String value) {
return EncryptHelper.decryptByBase64(value);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# AES 算法实现
public class AesEncryptor extends AbstractEncryptor {
private final EncryptContext context;
public AesEncryptor(EncryptContext context) {
super(context);
this.context = context;
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.AES;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return EncryptHelper.encryptByAesHex(value, context.getPassword());
} else {
return EncryptHelper.encryptByAes(value, context.getPassword());
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return EncryptHelper.decryptByAes(value, context.getPassword());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# RSA 算法
public class RsaEncryptor extends AbstractEncryptor {
private final EncryptContext context;
public RsaEncryptor(EncryptContext context) {
super(context);
String privateKey = context.getPrivateKey();
String publicKey = context.getPublicKey();
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
}
this.context = context;
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.RSA;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return EncryptHelper.encryptByRsaHex(value, context.getPublicKey());
} else {
return EncryptHelper.encryptByRsa(value, context.getPublicKey());
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return EncryptHelper.decryptByRsa(value, context.getPrivateKey());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# SM2 算法
public class Sm2Encryptor extends AbstractEncryptor {
private final EncryptContext context;
public Sm2Encryptor(EncryptContext context) {
super(context);
String privateKey = context.getPrivateKey();
String publicKey = context.getPublicKey();
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
}
this.context = context;
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.SM2;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return EncryptHelper.encryptBySm2Hex(value, context.getPublicKey());
} else {
return EncryptHelper.encryptBySm2(value, context.getPublicKey());
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return EncryptHelper.decryptBySm2(value, context.getPrivateKey());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# SM4 算法
public class Sm4Encryptor extends AbstractEncryptor {
private final EncryptContext context;
public Sm4Encryptor(EncryptContext context) {
super(context);
this.context = context;
}
/**
* 获得当前算法
*/
@Override
public AlgorithmType algorithm() {
return AlgorithmType.SM4;
}
/**
* 加密
*
* @param value 待加密字符串
* @param encodeType 加密后的编码格式
*/
@Override
public String encrypt(String value, EncodeType encodeType) {
if (encodeType == EncodeType.HEX) {
return EncryptHelper.encryptBySm4Hex(value, context.getPassword());
} else {
return EncryptHelper.encryptBySm4(value, context.getPassword());
}
}
/**
* 解密
*
* @param value 待加密字符串
*/
@Override
public String decrypt(String value) {
return EncryptHelper.decryptBySm4(value, context.getPassword());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 加解密工具类
上面封装了很多的算法类,每个算法有自己的 API,那么封装了一个加解密工具类,对外提供 API 调用每个算法的 API。
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class EncryptHelper {
/**
* 公钥
*/
public static final String PUBLIC_KEY = "publicKey";
/**
* 私钥
*/
public static final String PRIVATE_KEY = "privateKey";
/**
* Base64 加密
*
* @param data 待加密数据
* @return 加密后字符串
*/
public static String encryptByBase64(String data) {
return Base64.encode(data, StandardCharsets.UTF_8);
}
/**
* Base64 解密
*
* @param data 待解密数据
* @return 解密后字符串
*/
public static String decryptByBase64(String data) {
return Base64.decodeStr(data, StandardCharsets.UTF_8);
}
/**
* AES 加密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptByAes(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES 需要传入秘钥信息");
}
// aes 算法的秘钥要求是 16 位、24 位、32 位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES 秘钥长度要求为 16 位、24 位、32 位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
}
/**
* AES 加密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByAesHex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES 需要传入秘钥信息");
}
// aes 算法的秘钥要求是 16 位、24 位、32 位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES 秘钥长度要求为 16 位、24 位、32 位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/**
* AES 解密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 解密后字符串
*/
public static String decryptByAes(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES 需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES 秘钥长度要求为 16 位、24 位、32 位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
}
/**
* sm4 加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm4(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4 需要传入秘钥信息");
}
// sm4 算法的秘钥要求是 16 位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4 秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
}
/**
* sm4 加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm4Hex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4 需要传入秘钥信息");
}
// sm4 算法的秘钥要求是 16 位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4 秘钥长度要求为 16 位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/**
* sm4 解密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 解密后字符串
*/
public static String decryptBySm4(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4 需要传入秘钥信息");
}
// sm4 算法的秘钥要求是 16 位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4 秘钥长度要求为 16 位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
}
/**
* 产生sm2加解密需要的公钥和私钥
*
* @return 公私钥Map
*/
public static Map<String, String> generateSm2Key() {
Map<String, String> keyMap = new HashMap<>(2);
SM2 sm2 = SmUtil.sm2();
keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
return keyMap;
}
/**
* sm2 公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm2(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("SM2 需要传入公钥进行加密");
}
SM2 sm2 = SmUtil.sm2(null, publicKey);
return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* sm2 公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySm2Hex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("SM2 需要传入公钥进行加密");
}
SM2 sm2 = SmUtil.sm2(null, publicKey);
return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* sm2 私钥解密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 解密后字符串
*/
public static String decryptBySm2(String data, String privateKey) {
if (StrUtil.isBlank(privateKey)) {
throw new IllegalArgumentException("SM2 需要传入私钥进行解密");
}
SM2 sm2 = SmUtil.sm2(privateKey, null);
return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
}
/**
* 产生 RSA 加解密需要的公钥和私钥
*
* @return 公私钥Map
*/
public static Map<String, String> generateRsaKey() {
Map<String, String> keyMap = new HashMap<>(2);
RSA rsa = SecureUtil.rsa();
keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
return keyMap;
}
/**
* rsa 公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用 Base64 编码
*/
public static String encryptByRsa(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("RSA 需要传入公钥进行加密");
}
RSA rsa = SecureUtil.rsa(null, publicKey);
return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* rsa 公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用 Hex 编码
*/
public static String encryptByRsaHex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("RSA 需要传入公钥进行加密");
}
RSA rsa = SecureUtil.rsa(null, publicKey);
return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* rsa 私钥解密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 解密后字符串
*/
public static String decryptByRsa(String data, String privateKey) {
if (StrUtil.isBlank(privateKey)) {
throw new IllegalArgumentException("RSA 需要传入私钥进行解密");
}
RSA rsa = SecureUtil.rsa(privateKey, null);
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
}
/**
* md5 加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用 Hex 编码
*/
public static String encryptByMd5(String data) {
return SecureUtil.md5(data);
}
/**
* sha256 加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用 Hex 编码
*/
public static String encryptBySha256(String data) {
return SecureUtil.sha256(data);
}
/**
* sm3 加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用 Hex 编码
*/
public static String encryptBySm3(String data) {
return SmUtil.sm3(data);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# 接口数据加解密
一般重要的数据,在前后端交互的时候需要加解密:
- 前端提交数据到后端前,先加密再提交,后端拿到数据后,要进行解密
- 后端返回数据给前端前,先加密再提交,前端拿到数据后,要进行解密
加解密有很多算法方式,这里采用 Base64 算法、AES 算法、RSA 算法、
接口数据加解密实现步骤:
- 提供
ApiEncrypt
注解,在方法上使用后代表开启接口数据加密功能,里面有两个属性:request(对请求数据解密,非 GET 请求)、response(对响应的数据加密) - 利用 Filter 过滤器来对有
ApiEncrypt
注解的请求数据进行解密,响应数据进行加密 - 加密解密采用 AES 算法,密钥是随机生成的 32 字符,该密钥 Base64 加密,然后又采用 RSA 公钥加密后传输。私钥在前端持有。解密密钥时,使用私钥解密。公钥为前端持有
# 注解
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiEncrypt {
/**
* 请求体数据是否进行解密,默认不解密,为 true 时解密
*/
boolean request() default false;
/**
* 响应数据是否加密后返回,默认加密,为 true 时加密
*/
boolean response() default true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 配置项
支持 Spring Boot 的 application 配置属性
@Data
@ConfigurationProperties(prefix = "api-decrypt")
public class ApiDecryptProperties {
/**
* 加密开关
*/
private Boolean enabled;
/**
* 头部标识,告诉系统是否解密传过来的数据
*/
private String headerFlag;
/**
* 响应加密公钥
*/
private String publicKey;
/**
* 请求解密私钥
*/
private String privateKey;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 加解密缓存
项目初始化的时候,扫描有 ApiEncrypt 注解的属性,将其加解密算法等缓存起来,这样数据处理时,直接从缓存获取加解密信息。
@Data
public class EncryptContext {
/**
* 默认算法
*/
private AlgorithmType algorithm;
/**
* 安全秘钥
*/
private String password;
/**
* 公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
/**
* 编码方式,base64/hex
*/
private EncodeType encode;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
实现缓存的管理类,包括缓存 数据库字段值加密 的加解密信息
@Slf4j
@NoArgsConstructor
public class EncryptorManager {
/**
* 缓存加密器
*/
Map<EncryptContext, Encryptor> encryptorMap = new ConcurrentHashMap<>();
/**
* 类加密字段缓存
*/
Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
/**
* 构造方法传入类加密字段缓存
*
* @param typeAliasesPackage 实体类包
*/
public EncryptorManager(String typeAliasesPackage) {
scanEncryptClasses(typeAliasesPackage);
}
/**
* 获取类加密字段缓存
*/
public Set<Field> getFieldCache(Class<?> sourceClazz) {
if (ObjectUtil.isNotNull(fieldCache)) {
return fieldCache.get(sourceClazz);
}
return null;
}
/**
* 注册加密执行者到缓存
*
* @param encryptContext 加密执行者需要的相关配置参数
*/
public Encryptor registAndGetEncryptor(EncryptContext encryptContext) {
if (encryptorMap.containsKey(encryptContext)) {
return encryptorMap.get(encryptContext);
}
Encryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
encryptorMap.put(encryptContext, encryptor);
return encryptor;
}
/**
* 移除缓存中的加密执行者
*
* @param encryptContext 加密执行者需要的相关配置参数
*/
public void removeEncryptor(EncryptContext encryptContext) {
this.encryptorMap.remove(encryptContext);
}
/**
* 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。
*
* @param value 待加密的值
* @param encryptContext 加密相关的配置信息
*/
public String encrypt(String value, EncryptContext encryptContext) {
Encryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.encrypt(value, encryptContext.getEncode());
}
/**
* 根据配置进行解密
*
* @param value 待解密的值
* @param encryptContext 加密相关的配置信息
*/
public String decrypt(String value, EncryptContext encryptContext) {
Encryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.decrypt(value);
}
/**
* 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体
*/
private void scanEncryptClasses(String typeAliasesPackage) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
try {
for (String packagePattern : packagePatternArray) {
String path = ClassUtils.convertClassNameToResourcePath(packagePattern);
Resource[] resources = resolver.getResources(classpath + path + "/*.class");
for (Resource resource : resources) {
ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata();
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
Set<Field> encryptFieldSet = getEncryptFieldSetFromClazz(clazz);
if (CollUtil.isNotEmpty(encryptFieldSet)) {
fieldCache.put(clazz, encryptFieldSet);
}
}
}
} catch (Exception e) {
log.error("初始化数据安全缓存时出错:{}", e.getMessage());
}
}
/**
* 获得一个类的加密字段集合
*/
private Set<Field> getEncryptFieldSetFromClazz(Class<?> clazz) {
Set<Field> fieldSet = new HashSet<>();
// 判断clazz如果是接口,内部类,匿名类就直接返回
if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
return fieldSet;
}
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
fieldSet.addAll(Arrays.asList(fields));
clazz = clazz.getSuperclass();
}
fieldSet = fieldSet.stream().filter(field ->
field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
.collect(Collectors.toSet());
for (Field field : fieldSet) {
field.setAccessible(true);
}
return fieldSet;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# 过滤器
需要拦截 Request 和 Response 里的数据来进行处理,而 Spring MVC 只允许获取一次 Request 对象,如果不做如何处理,直接拦截到 Request 对象,那么项目里将无法二次使用 Request,因为 Request 的流被读取后,将无法二次读取,于是我们需要包装 Request 一层,读取流对象后,返回一个新的流对象,这个新的流对象就是原本的流对象(代理),这样将包装类返回给项目的时候,依然可以再次获取 Request 对象。Response 同理。
# 请求体包装类,实现解密功能
public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
private final byte[] body;
/**
* 采用 AES 加密数据,加密使用的密钥由 RSA 加密而得到
*
* @param request request
* @param privateKey RSA 私钥 (用于解密 AES 秘钥)
* @param headerFlag 请求头标志
*/
public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException {
super(request);
// 获取 AES 密码,该密码采用 RSA 加密
String headerRsa = request.getHeader(headerFlag);
String decryptAes = EncryptHelper.decryptByRsa(headerRsa, privateKey);
// 解密 AES 密码
String aesPassword = EncryptHelper.decryptByBase64(decryptAes);
request.setCharacterEncoding("UTF-8");
byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
String requestBody = new String(readBytes, StandardCharsets.UTF_8);
// 解密 body 采用 AES 加密
String decryptBody = EncryptHelper.decryptByAes(requestBody, aesPassword);
body = decryptBody.getBytes(StandardCharsets.UTF_8);
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public int getContentLength() {
return body.length;
}
@Override
public long getContentLengthLong() {
return body.length;
}
@Override
public String getContentType() {
return MediaType.APPLICATION_JSON_VALUE;
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() {
return bais.read();
}
@Override
public int available() {
return body.length;
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 响应体包装类,实现加密功能
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
private final ByteArrayOutputStream byteArrayOutputStream;
private final ServletOutputStream servletOutputStream;
private final PrintWriter printWriter;
public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {
super(response);
this.byteArrayOutputStream = new ByteArrayOutputStream();
this.servletOutputStream = this.getOutputStream();
this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));
}
/**
* 获取加密内容,加密采用 AES 加密,加密用的密钥由 RSA 公钥生成
*
* @param servletResponse response
* @param publicKey RSA 公钥 (用于加密 AES 秘钥)
* @param headerFlag 请求头标志
* @return 加密内容
*/
public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {
// 生成秘钥
String aesPassword = RandomUtil.randomString(32);
// 秘钥使用 Base64 编码
String encryptAes = EncryptHelper.encryptByBase64(aesPassword);
// Rsa 公钥加密 Base64 编码
String encryptPassword = EncryptHelper.encryptByRsa(encryptAes, publicKey);
// 设置响应头
servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
servletResponse.setHeader(headerFlag, encryptPassword);
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
servletResponse.setHeader("Access-Control-Allow-Methods", "*");
servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
// 获取原始内容
String originalBody = this.getContent();
// 对内容进行加密
return EncryptHelper.encryptByAes(originalBody, aesPassword);
}
@Override
public PrintWriter getWriter() {
return printWriter;
}
@Override
public void flushBuffer() throws IOException {
if (servletOutputStream != null) {
servletOutputStream.flush();
}
if (printWriter != null) {
printWriter.flush();
}
}
@Override
public void reset() {
byteArrayOutputStream.reset();
}
public byte[] getResponseData() throws IOException {
flushBuffer();
return byteArrayOutputStream.toByteArray();
}
public String getContent() throws IOException {
flushBuffer();
return byteArrayOutputStream.toString();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() {
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
byteArrayOutputStream.write(b);
}
@Override
public void write(byte[] b) throws IOException {
byteArrayOutputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
byteArrayOutputStream.write(b, off, len);
}
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 过滤器实现类
是西安 Servlet 提供的 Filter 接口来是实现拦截功能。
public class CryptoFilter implements Filter {
private final ApiDecryptProperties properties;
public CryptoFilter(ApiDecryptProperties properties) {
this.properties = properties;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse) response;
// 获取加密注解
ApiEncrypt apiEncrypt = getApiEncryptAnnotation(servletRequest);
boolean requestFlag = Objects.nonNull(apiEncrypt) && apiEncrypt.request();
boolean responseFlag = Objects.nonNull(apiEncrypt) && apiEncrypt.response();
ServletRequest requestWrapper = null;
ServletResponse responseWrapper = null;
EncryptResponseBodyWrapper responseBodyWrapper = null;
// 是否执行解密 && 是否为 put 或者 post 请求
if (requestFlag && (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod()))) {
// 是否存在解密标头
String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
if (StringUtil.hasText(headerValue)) {
// 请求解密
requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
} else if (requestFlag) {
// 是否有注解,有就报错,没有放行
HandlerExceptionResolver exceptionResolver = SpringHelper.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
exceptionResolver.resolveException(
servletRequest, servletResponse, null,
new ServiceException(ResponseStatusEnum.REQ_REJECT.getCode(), ResponseStatusEnum.REQ_REJECT.getStatus(), "没有访问权限,请联系管理员授权"));
return;
}
}
// 判断是否响应加密
if (responseFlag) {
responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
responseWrapper = responseBodyWrapper;
}
chain.doFilter(
ObjectUtil.defaultIfNull(requestWrapper, request),
ObjectUtil.defaultIfNull(responseWrapper, response));
if (responseFlag) {
servletResponse.reset();
// 对原始内容加密
String encryptContent = responseBodyWrapper.getEncryptContent(
servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
// 对加密后的内容写出
servletResponse.getWriter().write(encryptContent);
}
}
/**
* 获取 ApiEncrypt 注解
*/
private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
RequestMappingHandlerMapping handlerMapping = SpringHelper.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
// 获取注解
try {
HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
if (Objects.nonNull(mappingHandler)) {
Object handler = mappingHandler.getHandler();
// 从 handler 获取注解
if (handler instanceof HandlerMethod handlerMethod) {
return handlerMethod.getMethodAnnotation(ApiEncrypt.class);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
@Override
public void destroy() {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 容器装配
最后我们将实现的过滤器功能注入到 Spring 的容器里。
@AutoConfiguration
@EnableConfigurationProperties(ApiDecryptProperties.class)
@ConditionalOnProperty(value = "api-decrypt.enabled", havingValue = "true")
public class ApiDecryptAutoConfiguration {
@Bean
public FilterRegistrationBean<CryptoFilter> cryptoFilterRegistration(ApiDecryptProperties properties) {
FilterRegistrationBean<CryptoFilter> registration = new FilterRegistrationBean<>();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new CryptoFilter(properties));
registration.addUrlPatterns("/*");
registration.setName("cryptoFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
return registration;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 数据库字段值加解密
- 实现 EncryptField 注解,在字段使用后代表开启字段值加解密功能,具体加密规则在 EncryptField 里传入
- 实现 Mybatis insert、update 拦截器和 select 拦截器,前者对有 EncryptField 注解的字段值进行加密,后者对有 EncryptField 注解的字段值进行解密
- 内置缓存功能,在项目启动的时候,扫描实体类,将带有 EncryptField 注解的字段在 EncryptorManager 类里进行缓存,在第 2 步的拦截器中,进行获取并处理加解密
注意事项:第 3 点说明了需要扫描实体类并缓存,因此使用该功能,需要在 application 配置文件指定有 EncryptField 的实体类的包名。
# 注解
数据库数据加解密注解
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
/**
* 加密算法
*/
AlgorithmType algorithm() default AlgorithmType.DEFAULT;
/**
* 秘钥。AES、SM4 需要
*/
String password() default "";
/**
* 公钥。RSA、SM2 需要
*/
String publicKey() default "";
/**
* 私钥。RSA、SM2 需要
*/
String privateKey() default "";
/**
* 编码方式。对加密算法为 base64 不起作用
*/
EncodeType encode() default EncodeType.DEFAULT;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 枚举
编码类型枚举类
public enum EncodeType {
/**
* 默认使用 yml 配置
*/
DEFAULT,
/**
* base64 编码
*/
BASE64,
/**
* 16 进制编码
*/
HEX;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 配置项
支持 Spring Boot 的 application 配置属性
@Data
@ConfigurationProperties(prefix = "mybatis-encryptor")
public class EncryptorProperties {
/**
* 过滤开关
*/
private Boolean enable;
/**
* 默认算法
*/
private AlgorithmType algorithm;
/**
* 安全秘钥
*/
private String password;
/**
* 公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
/**
* 编码方式,base64/hex
*/
private EncodeType encode;
/**
* 加密实体类包路径
*/
private String classPackage;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Mybatis 拦截器
Mybatis 提供了进和出数据库的拦截器 Interceptor 接口,需要自定义类实现它,这样 Mybatis 会在数据进出数据库时候调用。
MybatisDecryptInterceptor 类需要对 Insert、Update 到数据库的数据进行拦截,然后加密
@Slf4j
@Intercepts({@Signature(
type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class})
})
@AllArgsConstructor
public class MybatisDecryptInterceptor implements Interceptor {
private final EncryptorManager encryptorManager;
private final EncryptorProperties defaultProperties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取执行 mysql 执行结果
Object result = invocation.proceed();
if (result == null) {
return null;
}
decryptHandler(result);
return result;
}
/**
* 解密对象
*
* @param sourceObject 待加密对象
*/
private void decryptHandler(Object sourceObject) {
if (Objects.isNull(sourceObject)) {
return;
}
if (sourceObject instanceof Map<?, ?> map) {
new HashSet<>(map.values()).forEach(this::decryptHandler);
return;
}
if (sourceObject instanceof List<?> list) {
if(CollUtil.isEmpty(list)) {
return;
}
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
Object firstItem = list.get(0);
if (Objects.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
return;
}
list.forEach(this::decryptHandler);
return;
}
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
if(Objects.isNull(fields)){
return;
}
try {
for (Field field : fields) {
field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));
}
} catch (Exception e) {
log.error("处理解密字段时出错", e);
}
}
/**
* 字段值进行加密。通过字段的批注注册新的加密算法
*
* @param value 待加密的值
* @param field 待加密字段
* @return 加密后结果
*/
private String decryptField(String value, Field field) {
if (Objects.isNull(value)) {
return null;
}
EncryptField encryptField = field.getAnnotation(EncryptField.class);
EncryptContext encryptContext = new EncryptContext();
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
encryptContext.setPassword(!StringUtil.hasText(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
encryptContext.setPrivateKey(!StringUtil.hasText(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
encryptContext.setPublicKey(!StringUtil.hasText(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
return this.encryptorManager.decrypt(value, encryptContext);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
MybatisEncryptInterceptor 类需要对 Select 出数据库的数据进行拦截,然后解密
@Slf4j
@Intercepts({@Signature(
type = ParameterHandler.class,
method = "setParameters",
args = {PreparedStatement.class})
})
@AllArgsConstructor
public class MybatisEncryptInterceptor implements Interceptor {
private final EncryptorManager encryptorManager;
private final EncryptorProperties defaultProperties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation;
}
@Override
public Object plugin(Object target) {
if (target instanceof ParameterHandler parameterHandler) {
// 进行加密操作
Object parameterObject = parameterHandler.getParameterObject();
if (Objects.nonNull(parameterObject) && !(parameterObject instanceof String)) {
encryptHandler(parameterObject);
}
}
return target;
}
/**
* 加密对象
*
* @param sourceObject 待加密对象
*/
private void encryptHandler(Object sourceObject) {
if (Objects.isNull(sourceObject)) {
return;
}
if (sourceObject instanceof Map<?, ?> map) {
new HashSet<>(map.values()).forEach(this::encryptHandler);
return;
}
if (sourceObject instanceof List<?> list) {
if(CollUtil.isEmpty(list)) {
return;
}
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
Object firstItem = list.get(0);
if (Objects.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
return;
}
list.forEach(this::encryptHandler);
return;
}
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是 typeAliasesPackage 写错)
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
if(Objects.isNull(fields)){
return;
}
try {
for (Field field : fields) {
field.set(sourceObject, encryptField(Convert.toStr(field.get(sourceObject)), field));
}
} catch (Exception e) {
log.error("处理加密字段时出错", e);
}
}
/**
* 字段值进行加密。通过字段的批注注册新的加密算法
*
* @param value 待加密的值
* @param field 待加密字段
* @return 加密后结果
*/
private String encryptField(String value, Field field) {
if (Objects.isNull(value)) {
return null;
}
EncryptField encryptField = field.getAnnotation(EncryptField.class);
EncryptContext encryptContext = new EncryptContext();
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
encryptContext.setPassword(!StringUtil.hasText(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
encryptContext.setPrivateKey(!StringUtil.hasText(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
encryptContext.setPublicKey(!StringUtil.hasText(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
return this.encryptorManager.encrypt(value, encryptContext);
}
@Override
public void setProperties(Properties properties) {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# 容器装配
将实现了 Mybatis 的拦截器注入到 Spring 容器里。
@AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
@EnableConfigurationProperties(EncryptorProperties.class)
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
@Slf4j
@RequiredArgsConstructor
public class EncryptorAutoConfiguration {
private final EncryptorProperties properties;
@Bean
public EncryptorManager encryptorManager() {
return new EncryptorManager(properties.getClassPackage());
}
@Bean
public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
return new MybatisEncryptInterceptor(encryptorManager, properties);
}
@Bean
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
return new MybatisDecryptInterceptor(encryptorManager, properties);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 扫描容器装配类
Spring Boot 3.x 需要在 resource 下建立 META-INF/spring
路径,然后创建 org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件,内容为
cn.youngkbt.encrypt.config.EncryptorAutoConfiguration
cn.youngkbt.encrypt.config.ApiDecryptAutoConfiguration
2
这样 Spring 会自动扫描该文件的两个容器装配类,将里面涉及的类注入到 Spring 容器。
# 使用案例
接口数据加解密使用:
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Validated
public class AuthController {
@PostMapping("/login")
@ApiEncrypt(request = true, response = false)
public String login(@Valid @RequestBody LoginUserDTO loginUserDTO) {
return "执行登录";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
使用了 ApiEncrypt
注解,代表前端发生的数据需要进行解密,后端返回的数据不需要加密。
数据库字段值加解密使用:
@Data
@TableName("test_encrypt")
public class DemoEncryptPO {
@TableId
private Long id;
@EncryptField(algorithm = AlgorithmType.RSA,
privateKey = """
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCcHvPEBJOit8uM
OJcL/6LwxBGrslfVU6iL/ka7W8Ux5ifr/lJUe+e0atMFb9hYL5VbfbexdI189uEu
UCT9U9aDgPW8A8SWg7DR0mXT/+tEnnl/RdsaeFke4HTDwOZJaBP4ljE0+pJnH5Xj
qxT803qo5EtXOdjv6fyysutpsAYLPu65nwWnKIGLyFvdvHrNAsM6i2TSWqpU6AAK
6EN3Jht83T1PvJfBL7AanjhUGMSbCAzl6w5sh9IJw0SnOA1Nk1/nRqWwJSJYf8Ri
3WvuRPUq8rjwU1tpvFNndlNtrtZeheCxBTAqpdq3RIHMkYHb/A7rfhinEqAJ+J6m
atYJ/tdFAgMBAAECggEAf6iboVwwsKxjh6w6brhBL2jUHjZ9tdVri9WHVurKi2vs
lv9lqOmMZK25EcgL+sgl9CMPks6AZ3+kT+9+35qvXOaViYD3PjA+5MOLOlgYtAp1
xMmFSNbB4Qqez/arF7lAI1QEtpJyJlrggMLDLZ1rvjC3HoMRzdPiKuk8sDYcKxKq
j1JIEULy3DYdPvrFI2FmcOsMtikP2bbKY4USStgA8r5KF7CtjJjjyjrkYwc61IIJ
haL+pFQf0Im+BEjPWoNXUK3LHNTIq8S8BirzibZmTfZCfnZzi7olvzJRc+l3XTz3
SgQb9Y/A3KOlrXd7bse+OzkuSDq9izR2x4ejayWO/QKBgQDPRTF7BBhrS6aq4Rkm
crtbYyHZ68kfyROZ3EIi+I/7nBPqnDaFYceEAIN2P6/25PPLBiEi5fI5DMnvzIT1
2PoFGSKTh6Y34VL6oFn3Fd5uHdmpLguaHu/lSN1M+Gry9E/mqh2zijZJLCKKWgY8
LX66eNnVdMftSMHFOSSli0KmHwKBgQDA00XwAls5r6/ClwxR7hDEGZ2iQ7L8UZWP
iT1aCPkKRvjjsO0TDyxmO+KJy9tMB1B49xpyPkApzErqueLyEEWYB0i3kSrTtGzM
J1MjMSPkRkgoJH4x4MW03eHPkDVa5q+oDBhVMX0yEQXWZ1iC3saRSgPlybE68Mja
FgwP2qpuGwKBgFPZ6T+cE4jsrPt6XyNXzQYWn646nj4WqbBYFAVzy0P+C2yhT8k8
GmwDjSt8bmKSkzIyQ5uLrSd5TgSOF8ghxFvlpEBM42i95kTwNBUqqrafqtuvfhAW
rfRzOtwVr6akQeLONX/ZzUZi7YJNEzKrMRadJ3scaHlNMt7n1DSIlyj/AoGABhEf
rDOGx0Pd1dOG0bUZ1fGwYgCbSxEOEZwR0BlkLIybHB7e9rCNhxHvSMKfPb8lKwkr
Tdjjj+0bllMO7urQJb5k7VGl9U9B9RJvrTXImVAUyR6M0ejuj4hDqJIy+48yi6kF
wvhxpfefJWXPBR8ZREz93mcAKoiU6Te0XXNV1W0CgYBVIsQGigYQTDiUpkL0mBHH
+10kMc/NWgOYPKJ5R8sOIC0YdDem368Udg6g0naOpyIQX9HR7knERtw9/fwWWdcs
mCP8Xuw4gHoOjjlVWPGM4Gx5f7+MPxxdwUrTf0jnnuNWuSysQ+cfGkbuyot/8Ar7
zE90AMVK2EgAPpuYtxv9gA==
""",
publicKey = """
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnB7zxASTorfLjDiXC/+i
8MQRq7JX1VOoi/5Gu1vFMeYn6/5SVHvntGrTBW/YWC+VW323sXSNfPbhLlAk/VPW
g4D1vAPEloOw0dJl0//rRJ55f0XbGnhZHuB0w8DmSWgT+JYxNPqSZx+V46sU/NN6
qORLVznY7+n8srLrabAGCz7uuZ8FpyiBi8hb3bx6zQLDOotk0lqqVOgACuhDdyYb
fN09T7yXwS+wGp44VBjEmwgM5esObIfSCcNEpzgNTZNf50alsCUiWH/EYt1r7kT1
KvK48FNbabxTZ3ZTba7WXoXgsQUwKqXat0SBzJGB2/wO634YpxKgCfiepmrWCf7X
RQIDAQAB
"""
)
private String testKey;
@EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5")
private String value;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
使用 EncryptField
注解,代表 value 在存入数据库的时候采用 AES 加密,读取时采用 AES 解密。其中 password 为密钥,privateKey 为私钥,publicKey wei公钥