一、前言
最新公司为了提高代码安全性,要求数据库,kakfa, redis 的连接不能直接明文配置。找了很多方案最终发现这个 jasypt-spring-boot组件,但也被这个组件坑炸了,为什么呢?因为出现 死锁 的问题。线上直接P0级别事故,现在我们来复盘一下这个死锁问题。首先分享一下如何集成这个组件。
二、如何集成
1、引入jasypt-spring-boot加密组件的starter(特别说明出现死锁的版本:3.0.3)
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
2、jasypt常用参数说明
2.1、algorithm与 iv -generator-classname对应关系
algorithm | iv-generator-classname | 版本 |
PBEWithMD5AndDES | org.jasypt.iv.NoIvGenerator | 3.0.0已下版本 |
PBEWITHHMACSHA512ANDAES_256 | org.jasypt.iv.RandomIvGenerator | 3.0.0以上版本 |
如果对应错误启动的时候会报以下错误
Failed to bind properties under ‘xxx.xxx.xxx’ to java.lang.String
2.2、配置说明
#自定义加密密钥,对配置文件里的重要项进行加密
jasypt:
encryptor:
password: xiao #加密算法密钥
property:
prefix: ENC( #算法识别前缀,加密后的账号密码需要用这个包起来
suffix: ) #算法识别后缀
algorithm: PBEWITHHMACSHA512ANDAES_256 #默认加密算法
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
algorithm加密算法要与IvGeneratorClassName对应的值配套
2、正常的明文配置文件yml (安全级别为0)
server:
port: 10000
spring:
application:
name: xixien
datasource:
url: jdbc :mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
3、通过工具类生成加密后的账号,密码
package com.xixien.api.base.utils;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig;
public class JasyptUtils {
public static void main(String[] args) {
String username = encrypt("root");
String password = encrypt("123456");
System.out.println("加密后的账号:"+username);
System.out.println("加密后的密码:"+password);
System.out.println("加密前的账号:"+decrypt(username));
System.out.println("加密前的密码:"+decrypt(password));
}
/**
* 加密
* @param plaintext 明文密码
*
*/ public static String encrypt(String plaintext) {
//加密工具
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
//加密配置
EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
// 算法类型
//config.setAlgorithm("PBEWithMD5AndDES");
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
//生成秘钥的公钥
config.setPassword("xiao");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
//应用配置
encryptor.setConfig(config);
//加密
return encryptor.encrypt(plaintext);
}
/**
* 解密
*
* @param ciphertext 待解密秘钥
*
*/ public static String decrypt(String ciphertext) {
//加密工具
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
//加密配置
EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
//config.setAlgorithm("PBEWithMD5AndDES");
//生成秘钥的公钥
config.setPassword("xiao");
//加密算法要与IvGeneratorClassName对应的值配套
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
//应用配置
encryptor.setConfig(config);
//解密
return encryptor.decrypt(ciphertext);
}
}
main行数运行结果
替换名为配置
原来的明文密码值,改为 ENC(xxx) ,其中xxx是密文。
server:
port: 10000
spring:
application:
name: xixien
datasource:
url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: ENC(9dGDBltW5Q9ealpUkaoEQyup4CSDr7Gz+CKpat3Uq99Wwaz7In4PyKKPSJOs+stK)
password: ENC(q1KCCS7dSI1eznFC9i7ml3cuGRPjhXYswqkgJWAffMJlkS1I4E35GiQqsv8gHYpx)
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#自定义加密密钥,对配置文件里的重要项进行加密
jasypt:
encryptor:
password: xiao #加密算法密钥(例子我们放这里)
property:
prefix: ENC( #算法识别前缀,加密后的账号密码需要用这个包起来
suffix: ) #算法识别后缀
algorithm: PBEWITHHMACSHA512ANDAES_256 #默认加密算法
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
自定义加密前后缀:
默认前缀prefix=ENC( 后缀 suffix= ) 这个可以自己随心所欲的替换比如LOL()
jasypt:
encryptor:
property:
prefix: LOL( #算法识别前缀,加密后的账号密码需要用这个包起来
suffix: )
看完这些你是否思考了
三、如何更安全
1、使用自定义加密器
下面是一个自定义加密器bean,可以看到除了一些在yml文件中配置的属性还多了配置(加上了注释的代码),源码类 JasyptEncryptorConfigurationProperties中有些默认配置
@Configuration
public class XiaoXiongEncryptorCfg {
//默认bean为jasyptStringEncryptor
@Bean( name = "xiaoXiongEncryptorBean" )
public StringEncryptor xiaoXiongEncryptorBean() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword("xiao");
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
//源码默认值
config.setKeyObtentionIterations("1000");
//源码默认值
config.setPoolSize("1");
//源码默认值
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
//源码默认值
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
}
2、密钥不放在配置文件中
- 方式一 :直接作为程序启动时的 命令行参数 来带入
java -jar project.jar –jasypt.encryptor.password=xiao
- 方式二 :直接作为程序启动时的 应用环境变量 来带入
java -Djasypt.encryptor.password=xiao -jar project.jar
- 方式三 :甚至可以作为 系统环境变量 的方式来带入
比方说,我们提前设置好系统环境变量JASYPT_ENCRYPTOR_PASSWORD = xiao,则直接在Spring Boot的项目配置文件中做如下配置即可:
jasypt.encryptor.password= ${ JASYPT_ENCRYPTOR_PASSWORD }
死锁
我线上的某个业务触发了这个死锁。程序表现为假死,最后通过分析堆栈日志找到这个死锁。也在github上第277的issues发现了端倪。真是脑裂呀
1、原因
2、修改后的代码