对SpringWeb项目中SpringMVC注解(@RequestMapping
、@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
)的接口参数进行脱敏与加解密,仅限于Http请求中字符串类型的参数数据生效,可进行充分扩展,可自进行定义脱敏、加解密等。
1.0.0版本
内置加密方式:AES、RSA(默认加密模式)
高级用法:支持
1.0.1版本【latest-version】
内置加密方式:支持AES、RSA加密,新增国密SM2、SM4加密方式
增加内置加密器密钥生成:[内置加密器密钥生成](# 内置加密器密钥生成)
高级用法:支持,文档更新[自定义加密器](# 自定义加密器)配置及使用方式
日志输出:DEBUG级别支持详细日志方便调试
Maven:
依赖已发布至Maven中央仓库,可直接引入依赖。
https://mvnrepository.com/artifact/com.feizhaiyou.encrypt/encrypt-spring-boot-starter
<dependency>
<groupId>com.feizhaiyou.encrypt</groupId>
<artifactId>encrypt-spring-boot-starter</artifactId>
<version>${latest-version}</version>
</dependency>
Gradle
请自行替换版本号
// https://mvnrepository.com/artifact/com.feizhaiyou.encrypt/encrypt-spring-boot-starter
implementation group: 'com.feizhaiyou.encrypt', name: 'encrypt-spring-boot-starter', version: '1.0.0'
本地:
git
拉取代码到本地,执行mvn install
(选择profile
为local
,例如:mvn clean install -Plocal
)打包下载到本地Maven仓库,项目maven pom引入
<dependency>
<groupId>com.feizhaiyou.encrypt</groupId>
<artifactId>encrypt-spring-boot-starter</artifactId>
<version>${latest-version}</version>
</dependency>
maven中央仓库不支持自行提交jar包,因此我们只能将jar包发布到它指定的第三方maven仓库(sonatype),这会导致maven中央和其他镜像仓库(阿里云等)有同步延迟。解决方案:
使用源码直接安装到本地maven仓库
修改远程maven仓库地址
<mirror>
<id>central</id>
<name>sonatype-central</name>
<url>https://s01.oss.sonatype.org/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
对称加密:AES 密钥请自行生成【[内置加密器密钥生成](# 内置加密器密钥生成)】
fzy:
security:
#是否启动脱敏与加密,默认true
enable: true
#加密后的编码模式:base64、hex,默认base64
mode: base64
#参数字段递归深度,默认5
max-deep: 5
#加密方式:AES、RSA,默认AES(1.0.1新增内置SM2和SM4加密方式)
type: AES
#对称加密密钥 密钥请自行生成
secret: +6cuvzvyrFZpRG9pf3r7eQ==
#脱敏与加解密实体类所在包 支持包前缀和正则匹配
class-package:
- com.feizhaiyou.test.entity
#字符集
charset: UTF-8
非对称加密:RSA privateKey与publicKey请自行生成【[内置加密器密钥生成](# 内置加密器密钥生成)】
fzy:
security:
enable: true
mode: hex
max-deep: 5
type: RSA
class-package:
- com.feizhaiyou.test.entity
charset: UTF-8
public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQwb536Gb2eZ60H/4VQRJUpod0M04yCR6IaEdgAbBACWvc5E2s7gCipO9IQB5tiU08JSQ2Mmfg88uGffK6z5YcKJ/CNWByiNHL3B2GzsgMMBh2a5Dba+KqA3ZFkeXhkw8htej18mM4Xw1KGytAni7sypzjynC+4/m7FcewB6BBxQIDAQAB
private-key: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJDBvnfoZvZ5nrQf/hVBElSmh3QzTjIJHohoR2ABsEAJa9zkTazuAKKk70hAHm2JTTwlJDYyZ+Dzy4Z98rrPlhwon8I1YHKI0cvcHYbOyAwwGHZrkNtr4qoDdkWR5eGTDyG16PXyYzhfDUobK0CeLuzKnOPKcL7j+bsVx7AHoEHFAgMBAAECgYBC6av3lW/ywuk/bgJvJLMqegfnCMg+jMSWXU2Q7XbhQvmfE9Fr/GBCzmblSO5Hx83x3WFNxCMMcuVDd/i5rbc+/fKoYyEhuOI91bHRxWR2M1VkNAZpUMeccGM/g15/p0P4pNEFYEaXkUZ170Je8aaECmr8v24ukvGlKFE38RuWgQJBANZCIkBxtY3H3CnT4LXX8NaJlWh7SpSZJsazwblDbubCMBJ+3qSaKxCE9v3d8F9QVPvZKgwd7uWIXHtcDygigVUCQQCs9U8NpV0VgZKbJzEuxaelpmi7eCe02hc5C1NVxQ14vRmV8na//GYOI6s0fNZjkPPGdRvmsTRpDYAeR3U/qX6xAkA+aed2ZGlh0s2Uta7o6e2zYULPu9Ke9Poxud197WHaAMlmKeULiAlxd3pHu6Yw7cLD1qAEBZg47v8ZxFh59Ys9AkEAjk4fxxiB6An+OA4dF2ClOVQb4/NOqCyw4syAupcxKGBeRtOCBSCET6nlWYBFXRKMoIMGe97dQnpLKZ6dx82LYQJBAJADjI2XGtg5HlgIzOLMMsTJ4+3OqCiJLoNAkmozckSQveyuh0Jh0Vvs1nTdfR1DMbsrIR2Me/ugPXzVvtEpU80=
public static void main(String[] args) {
// 调用对应的加密器的generateKey()方法即可
Map<String, String> keyMap = SM2Processor.generateKey();
System.out.println("SM2 publicKey: " + keyMap.get("PUB"));
System.out.println("SM2 privateKey: " + keyMap.get("PRV"));
// 支持指定密钥字符串的编码模式:SecurityMode.BASE64, SecurityMode.HEX
String key = SM4Processor.generateKey(SecurityMode.HEX);
System.out.println("SM4 secret: " + key);
}
说明:
调用内置加密器的
generateKey()
静态方法即可,对称加密直接返回密钥String
,非对称加密返回Map<String, String>
,map.get("PUB")
为生成的公钥,map.get("PRV")
为生成的私钥。内置加密器:
AES:
com.feizhaiyou.encrypt.codec.AESProcessor
RSA:
com.feizhaiyou.encrypt.codec.RSAProcessor
SM2:
com.feizhaiyou.encrypt.codec.SM2Processor
SM4:
com.feizhaiyou.encrypt.codec.SM4Processor
@Sensitive
在实体类属性或者接口方法(针对直接响应字符串的情况)上使用@Sensitive
注解即可。
注解属性说明:
参数名 | 类型 | 默认值 | 说明 |
---|---|---|---|
required | boolean | true | 是否开启字段加密,默认开启 |
type | String | com.feizhaiyou.encrypt.constants.SensitiveType.DEFUALT |
脱敏参数类型 |
注意:@Sensitive
注解必须用在字符串类型的参数字段或接口返回类型为字符串的接口方法上。
@Sensitive
注解package com.feizhaiyou.test.entity;
import com.feizhaiyou.encrypt.annotation.Sensitive;
import com.feizhaiyou.encrypt.constants.SensitiveType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author ls
* @since 2023-07-28
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String id;
@Sensitive(type = SensitiveType.CHINESE_NAME)
private String name;
@Sensitive(type = SensitiveType.ID_CARD)
private String idCard;
@Sensitive(type = SensitiveType.EMAIL)
private String email;
@Sensitive(type = SensitiveType.MOBILE_PHONE)
private String phone;
private List<Address> addressList;
}
package com.feizhaiyou.test.entity;
import com.feizhaiyou.encrypt.annotation.Sensitive;
import com.feizhaiyou.encrypt.constants.SensitiveType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author ls
* @since 2023-07-28
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String id;
@Sensitive(type = SensitiveType.ADDRESS)
private String address;
private String areaCode;
}
Controller
package com.feizhaiyou.test.controller;
import com.feizhaiyou.encrypt.annotation.Sensitive;
import com.feizhaiyou.test.entity.Address;
import com.feizhaiyou.test.entity.Result;
import com.feizhaiyou.test.entity.User;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.UUID;
/**
* @author ls
* @since 2023-07-28
*/
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/get/{id}")
public Result get(@PathVariable("id") String id) {
User user = User.builder()
.id(id)
.idCard("110222199703036666")
.email("243607654@qq.com")
.name("热巴")
.phone("13939393939")
.addressList(Arrays.asList(Address.builder()
.address("北京市东城区长安街")
.areaCode("100000")
.id(UUID.randomUUID().toString())
.build())).build();
return Result.OK(user);
}
}
PostMan调用测试:
@Sensitive
注解Controller
package com.feizhaiyou.test.controller;
import com.feizhaiyou.encrypt.annotation.Sensitive;
import com.feizhaiyou.test.entity.Address;
import com.feizhaiyou.test.entity.Result;
import com.feizhaiyou.test.entity.User;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.UUID;
/**
* @author ls
* @since 2023-07-21
*/
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/getAddress")
@Sensitive
public String getAddress() {
return "北京市东城区长安街";
}
}
PostMan调用测试:
@Security
在实体类属性或者接口方法(针对直接响应字符串的情况)上使用@Security
注解即可。
注解属性说明:
参数名 | 类型 | 默认值 | 说明 |
---|---|---|---|
encrypt | boolean | true | 是否对响应加密,默认开启 |
decrypt | boolean | true | 是否对请求解密,默认开启 |
注意:@Security
注解必须用在字符串类型的参数(实体类属性或方法参数)或接口返回类型为字符串的接口方法上。
@Security
注解package com.feizhaiyou.test.entity;
import com.feizhaiyou.encrypt.annotation.Security;
import com.feizhaiyou.encrypt.annotation.Sensitive;
import com.feizhaiyou.encrypt.constants.SensitiveType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String id;
// name 即加密又解密
@Security
private String name;
// idCard 不进行解密只加密
@Security(decrypt = false)
private String idCard;
// email 不进行加密只解密
@Security(encrypt = false)
private String email;
private String phone;
private List<Address> addressList;
}
package com.feizhaiyou.test.entity;
import com.feizhaiyou.encrypt.annotation.Security;
import com.feizhaiyou.encrypt.annotation.Sensitive;
import com.feizhaiyou.encrypt.constants.SensitiveType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author ls
* @since 2023-07-28
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String id;
@Security
private String address;
private String areaCode;
}
Controller
@PostMapping("/post/{id}")
public Result get(@PathVariable("id") String id, @RequestBody User user) {
System.out.println("id = " + id + ", user = " + user);
return Result.OK(user);
}
PostMan调用测试:
控制台输出:
@Security
注解Controller
@GetMapping("/getEmail")
@Security
public String getEmail(@Security String name) {
System.out.println("name = " + name);
return "243607654@qq.com";
}
PostMan调用测试:
控制台输出:
说明:
用在方法上,可以对接口响应的字符串进行加密
用在参数中,可以对接口请求参数的字符串进行解密
实现com.feizhaiyou.encrypt.codec.SecurityProcessor
接口,实现byte[] encrypt(byte[] data)
加密方法和byte[] decrypt(String text)
解密方法,例如:
package com.feizhaiyou.test.codec;
import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.DES;
import com.feizhaiyou.encrypt.codec.SecurityProcessor;
import com.feizhaiyou.encrypt.constants.SecurityMode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* 自定义加密器实现
* 加密模式:DES/CTS/PKCS5Padding
*
* @author ls
*/
@Data
@Slf4j
public class MyProcessor implements SecurityProcessor {
private String secret;
private String salt;
private DES des;
/**
* DES/CTS/PKCS5Padding
*
* @param secret 密钥
* @param salt 盐(偏移量)8字节
*/
public MyProcessor(String secret, String salt) {
this.secret = secret;
this.salt = salt;
this.des = new DES(Mode.CTS, Padding.PKCS5Padding, SecureUtil.decode(secret), salt.getBytes());
}
@Override
public byte[] decrypt(String text) {
return des.decrypt(text);
}
@Override
public byte[] encrypt(byte[] data) {
return des.encrypt(data);
}
/**
* 以base64编码生成 DES/CTS/PKCS5Padding 秘钥
*
* @return
*/
public static String generateKey() {
return generateKey(SecurityMode.BASE64);
}
/**
* 生成 DES/CTS/PKCS5Padding 秘钥
*
* @param type {@link SecurityMode} 编码模式
* @return
*/
public static String generateKey(SecurityMode type) {
byte[] keyBytes = SecureUtil.generateKey("DES/CTS/PKCS5Padding").getEncoded();
switch (type) {
case HEX:
return HexUtil.encodeHexStr(keyBytes);
case BASE64:
default:
return Base64Encoder.encode(keyBytes);
}
}
public static void main(String[] args) {
String secret = generateKey();
System.out.println(secret);
MyProcessor processor = new MyProcessor("KRxwmyr37N8=", "12345678");
byte[] encrypt = processor.encrypt("北京市东城区长安街".getBytes());
String encode1 = Base64Encoder.encode(encrypt);
System.out.println(encode1);
byte[] decrypt = processor.decrypt(encode1);
System.out.println(StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
}
}
package com.feizhaiyou.test;
import com.feizhaiyou.encrypt.codec.SecurityProcessor;
import com.feizhaiyou.test.codec.MyProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* TestApplication
*
* @author Jason
*/
@SpringBootApplication
@EnableAspectJAutoProxy
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class);
System.out.println("启动成功");
}
@Bean
@ConditionalOnMissingBean
// 定义fzy.security.type属性值为MY时加载此加密器
@ConditionalOnProperty(prefix = "fzy.security", name = "type", havingValue = "MY")
public SecurityProcessor myProcessor() {
// 密钥和加盐可从配置文件中读取
return new MyProcessor("KRxwmyr37N8=", "12345678");
}
}
server:
port: 1010
logging:
level:
# 日志输出
com.feizhaiyou.encrypt: debug
org.springframework: warn
fzy:
security:
enable: true
mode: base64
max-deep: 5
# 配置为自定义的加密器
type: MY
class-package:
- com.feizhaiyou.test.entity
charset: UTF-8
DEBUG级日志输出加载的自定义加密器
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。