[SpringBoot Advanced] SpringBoot integrates jasypt data desensitization
Configuration desensitization
scenes to be used
The database password is directly written in the application.yml configuration in clear text, which is a big challenge for security. Once the password is leaked, it will bring great security risks. Especially in some enterprises with high security requirements, we consider how to encrypt passwords.
The open source framework Jasypt can solve the above problems.
-
The Jasypt open source security framework is specially designed to handle Spring boot attribute encryption. Use a specific format to directly configure the ciphertext in the configuration file, and then when the application starts, Jasypt will automatically decrypt the password into plaintext for use by the program.
-
jasypt encrypts the same content with the same key (secretKey), and the ciphertext generated each time is different, but it is possible to decrypt the original content according to these ciphertexts.
Configuration Masking Practice
<!--配置文件加密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
Add the key configuration item to the configuration file jasypt.encryptor.password
, and replace the value that needs to be desensitized value
with the pre-encrypted content ENC(zxcvb/asdfg)
.
We can define this format at will. For example, if you want abc[zxcvb/asdfg]
the format, you only need to configure the prefix and suffix.
jasypt:
encryptor:
property:
prefix: "abc["
suffix: "]"
ENC(XXX)
jasypt
The format is mainly for the convenience of identifying whether the value needs to be decrypted. If it is not configured according to this format, the original value will be kept when the configuration item is loaded and will not be decrypted.
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
A pre-generated encrypted value can be generated by calling the API in the code
@Autowired
private StringEncryptor stringEncryptor;
@GetMapping("/encode")
public String encrypt(String content) {
String encryptStr = stringEncryptor.encrypt(content);
System.out.println("加密后的内容:" + encryptStr);
return "加密后的内容:" + encryptStr;
}
data desensitization
The private data of users in the production environment, such as mobile phone numbers, ID cards, or some account configuration information, must be desensitized without landing when entering the warehouse, that is, it must be desensitized in real time when entering our system.
User data enters the system, is desensitized and persisted to the database, and reverse decryption is performed when the user queries the data. This kind of scene generally requires global processing, so it AOP
is not suitable to use aspects to realize it.
First, customize two annotations @EncryptField
and @EncryptMethod
use them on field properties and methods respectively. The implementation idea is very simple. As long as the @EncryptMethod
annotations are applied to the method, check whether the input field is marked with @EncryptField
annotations, and if so, encrypt the corresponding field content.
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;
}
}