【SpringBoot高级篇】SpringBoot集成jasypt数据脱敏
- 配置脱敏
- 使用场景
- 配置脱敏实践
- 数据脱敏
- pom
- yml
- EncryptMethod
- EncryptField
- EncryptConstant
- EncryptHandler
- Person
- JasyptApplication
配置脱敏
使用场景
数据库密码直接明文写在application.yml配置中,对安全来说,是一个很大的挑战。一旦密码泄漏,将会带来很大的安全隐患。尤其在一些企业对安全性要求很高,因此我们就考虑如何对密码进行加密。
开源框架Jasypt可以解决上面的问题。
-
Jasypt 开源安全框架就是专门用于处理 Spring boot 属性加密的,在配置文件中使用特定格式直接配置密文,然后应用启动的时候,Jasypt 会自动将密码解密成明文供程序使用。
-
jasypt 同一个密钥(secretKey)对同一个内容执行加密,每次生成的密文都是不一样的,但是根据根据这些密文解密成原内容都是可以的。
配置脱敏实践
<!--配置文件加密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
配置文件加入秘钥配置项jasypt.encryptor.password
,并将需要脱敏的value
值替换成预先经过加密的内容ENC(zxcvb/asdfg)
。
这个格式我们是可以随意定义的,比如想要abc[zxcvb/asdfg]
格式,只要配置前缀和后缀即可。
jasypt:
encryptor:
property:
prefix: "abc["
suffix: "]"
ENC(XXX)
格式主要为了便于识别该值是否需要解密,如不按照该格式配置,在加载配置项的时候jasypt
将保持原值,不进行解密。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: ENC(ipHBo9hH/W756iU3HjAZJA==)
# 秘钥
jasypt:
encryptor:
password: csdn
预先生成的加密值,可以通过代码内调用API生成
@Autowired
private StringEncryptor stringEncryptor;
@GetMapping("/encode")
public String encrypt(String content) {
String encryptStr = stringEncryptor.encrypt(content);
System.out.println("加密后的内容:" + encryptStr);
return "加密后的内容:" + encryptStr;
}
数据脱敏
生产环境用户的隐私数据,比如手机号、身份证或者一些账号配置等信息,入库时都要进行不落地脱敏,也就是在进入我们系统时就要实时的脱敏处理。
用户数据进入系统,脱敏处理后持久化到数据库,用户查询数据时还要进行反向解密。这种场景一般需要全局处理,那么用AOP
切面来实现在适合不过了。
首先自定义两个注解@EncryptField
、@EncryptMethod
分别用在字段属性和方法上,实现思路很简单,只要方法上应用到@EncryptMethod
注解,则检查入参字段是否标注@EncryptField
注解,有则将对应字段内容加密。
pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
yml
# 秘钥
jasypt:
encryptor:
password: csdn
EncryptMethod
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod {
String type() default EncryptConstant.ENCRYPT;
}
EncryptField
@Documented
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
String[] value() default "";
}
EncryptConstant
public interface EncryptConstant {
// 加密
String ENCRYPT = "encrypt";
// 解密
String DECRYPT = "decrypt";
}
EncryptHandler
@Slf4j
@Aspect
@Component
public class EncryptHandler {
@Autowired
private StringEncryptor stringEncryptor;
@Pointcut("@annotation(cn.zysheep.annotation.EncryptMethod)")
public void pointCut() {
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
/**
* 加密
*/
Object[] encrypt = encrypt(joinPoint);
/**
* 解密
*/
Object decrypt = decrypt(joinPoint,encrypt);
return decrypt;
}
public Object[] encrypt(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
try {
if (args.length != 0) {
for (int i = 0; i < args.length; i++) {
Object o = args[i];
if (o instanceof String) {
args[i] = encryptValue(o);
} else {
args[i] = handler(o, EncryptConstant.ENCRYPT);
}
//TODO 其余类型自己看实际情况加
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return args;
}
public Object decrypt(ProceedingJoinPoint joinPoint,Object[] args) {
Object result = null;
try {
Object obj = joinPoint.proceed(args);
if (obj != null) {
if (obj instanceof String) {
result = decryptValue(obj);
} else {
result = handler(obj, EncryptConstant.DECRYPT);
}
//TODO 其余类型自己看实际情况加
}
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}
private Object handler(Object obj, String type) throws IllegalAccessException {
if (Objects.isNull(obj)) {
return null;
}
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
boolean annotationPresent = field.isAnnotationPresent(EncryptField.class);
if (annotationPresent) {
field.setAccessible(true);
String value;
String realValue = (String) field.get(obj);
if (EncryptConstant.DECRYPT.equals(type)) {
value = stringEncryptor.decrypt(realValue);
} else {
value = stringEncryptor.encrypt(realValue);
}
field.set(obj, value);
}
}
return obj;
}
public String encryptValue(Object realValue) {
String value = null;
try {
value = stringEncryptor.encrypt(String.valueOf(realValue));
} catch (Exception ex) {
return value;
}
return value;
}
public String decryptValue(Object realValue) {
String value = String.valueOf(realValue);
try {
value = stringEncryptor.decrypt(value);
} catch (Exception ex) {
return value;
}
return value;
}
}
Person
@Data
public class Person {
private Integer id;
@EncryptField
private String mobile;
@EncryptField
private String address;
private Integer age;
private BigDecimal wages;
}
JasyptApplication
@RestController
@SpringBootApplication
public class JasyptApplication {
@Autowired
private StringEncryptor stringEncryptor;
public static void main(String[] args) {
SpringApplication.run(JasyptApplication.class, args);
}
@GetMapping("/encode")
public String encrypt(String content) {
String encryptStr = stringEncryptor.encrypt(content);
System.out.println("加密后的内容:" + encryptStr);
return "加密后的内容:" + encryptStr;
}
@EncryptMethod
@PostMapping("/dataEnc")
public Person encrypt(@RequestBody Person person, @EncryptField String username) throws JsonProcessingException {
ObjectMapper json = new ObjectMapper();
String writeValueAsString = json.writeValueAsString(person);
System.out.println(writeValueAsString);
System.out.println(username);
return person;
}
@EncryptMethod
@GetMapping("/getParam")
public String getParam( @EncryptField String username) {
System.out.println("保存数据库业务操作===>username: "+username);
return username;
}
}