第三方库 jasypt
要实现该功能,我们要借助一个 Github 的第三方库 com.github.ulisesbocchio » jasypt-spring-boot-starter,Github地址为 https://github.com/ulisesbocchio/jasypt-spring-boot
Maven 地址如下:https://mvnrepository.com/artifact/com.github.ulisesbocchio/jasypt-spring-boot-starter
点击最新的版本号,会列出 maven 或 gradle 的依赖方式,拷贝即可
maven 的依赖方式
<!-- https://mvnrepository.com/artifact/com.github.ulisesbocchio/jasypt-spring-boot-starter -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
gradle 的依赖方式
// https://mvnrepository.com/artifact/com.github.ulisesbocchio/jasypt-spring-boot-starter
compile group: 'com.github.ulisesbocchio', name: 'jasypt-spring-boot-starter', version: '2.1.2'
jasypt 官网
在 http://www.jasypt.org/cli.html,可以查到它所支持的摘要算法和加密方式
摘要算法
- MD2
- MD5
- SHA
- SHA-256
- SHA-384
- SHA-512
加密方式
- PBEWITHMD5ANDDES
- PBEWITHMD5ANDTRIPLEDES
- PBEWITHSHA1ANDDESEDE
- PBEWITHSHA1ANDRC2_40
jsypt 的仓库路径
在 maven 中添加引用之后,jsypt 会自动被下载到 m2 仓库中,默认的地址为 C:\Users\【用户名】.m2\repository\org\jasypt\jasypt\1.9.3
命令行加密数据
java -cp 与 java -jar 的区别
- java -cp 和 -classpath 一样,是指定类运行所依赖其他类的路径,通常是类库,jar包之类
- java -jar 执行的适合,会用到 jar 包里面的 META-INF\MANIFEST.MF 文件,在该文件中,有一个叫Main-Class的参数,它说明了java -jar命令执行的类
cd C:\Users\【用户名】\.m2\repository\org\jasypt\jasypt\1.9.3
java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input=【待加密的数据】 password=【密码】algorithm=PBEWithMD5AndDES
实现自动解密
默认属性加密
创建包含加密数据的 yaml 文件
在 resources 的 config 目录下创建一个 encrypt.xml 文件(文件名随意),内容如下
spring:
datasource:
url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=mydatabase
username: bob
password: ENC(eGm+abBtq2NXGt7FZ3SDoEPpHaQ2OOpXZ/L16aSfBMc=)
ENC() 表示 括号内部的内容是加密的,spring boot 在初始化时会自动将其解密(括号内的内容用的就是我们之前通过命令行加密后的值)
启用加密配置文件
- @EnableEncryptableProperties 表示启用加密配置文件
- @EncryptablePropertySource 用来设置加密配置文件的路径
- @EncryptablePropertySources 如果有多个加密配置文件,则用花括号括起来,中间用逗号隔开每一个@EncryptablePropertySource
注意:只要有用到加密配置文件,前两者都不能省
@EnableEncryptableProperties
//@EncryptablePropertySource("classpath:/config/encrypt.yml")
@EncryptablePropertySource(value="classpath:/config/encrypt.yml")
//@EncryptablePropertySources({@EncryptablePropertySource("classpath:/config/encrypt.yml")})
public class EncryptionPropertyConfig { …… }
添加一个配置类,对加密器进行配置
@Configuration
@EnableEncryptableProperties
@EncryptablePropertySource("classpath:/config/encrypt.yml")
public class EncryptionPropertyConfig {
//这个bean的名字也不能随意更改,否则编译不过
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor() {
//集中式PBE字符串加密器
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
//简单字符串形式的PBC配置
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
//设置加密密码(大小写敏感),密码错误将导致程序异常
config.setPassword("YourPassword");
//设置加密算法
config.setAlgorithm("PBEWithMD5AndDES");
//设置获取加密密钥的哈希迭代次数
config.setKeyObtentionIterations("1000");
//设置线程池大小
config.setPoolSize("1");
//设置加密器的提供者
config.setProviderName("SunJCE");
//设置加盐的类名
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
//设置初始向量IV生成器的类名
config.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator");
//设置字节数组以base64格式输出
config.setStringOutputType("base64");
//将对应的配置设置到密码器上
encryptor.setConfig(config);
return encryptor;
}
}
添加一个访问控制器,当访问某个地址时,打印该变量
@RestController
public class TestController {
@Value("${spring.datasource.password}")
private String password;
@GetMapping("/testValue")
public String testValue() {
System.out.println(password);
return "";
}
}
改变加密标志前缀和后缀
默认的加密前缀是 “ENC(” ,后缀是")"。但有的时候我们需要自定义,方法是在 application.yml 后面添加一段
jasypt:
encryptor:
property:
prefix: "ENC@["
suffix: "]"
这样,加密前缀就改为了 “ENC@[”,而后缀就改为了 “]”,修改配置文件后重启即可生效
spring:
datasource:
url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=mydatabase
username: bob
password: ENC@[eGm+abBtq2NXGt7FZ3SDoEPpHaQ2OOpXZ/L16aSfBMc=]
自定义属性解密方式
@Configuration
@EnableEncryptableProperties
@EncryptablePropertySource("classpath:/config/encrypt.yml")
public class EncryptionPropertyConfig {
// 加密属性前缀
private static final String PREFIX = "ENC@[";
// 密钥
private static String PASSWORD = "YourPassword";
//同样名字不能随意修改,否则编译不通过
@Bean(name = "encryptablePropertyResolver")
public EncryptablePropertyResolver encryptablePropertyResolver() {
return new MyEncryptablePropertyResolver(PREFIX, PASSWORD);
}
private static class MyEncryptablePropertyResolver implements EncryptablePropertyResolver {
private final String prefix;
//PBE字符串加密器
private final PooledPBEStringEncryptor encryptor;
public MyEncryptablePropertyResolver(String prefix, String password) {
this.prefix = prefix;
// 集中式PBE字符串加密器
encryptor = new PooledPBEStringEncryptor();
// 简单字符串形式的PBC配置
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
// 设置加密密码(大小写敏感),密码错误将导致程序异常
config.setPassword("YourPassword");
// 设置加密算法
config.setAlgorithm("PBEWithMD5AndDES");
// 设置获取加密密钥的哈希迭代次数
config.setKeyObtentionIterations("1000");
// 设置线程池大小
config.setPoolSize("1");
// 设置加密器的提供者
config.setProviderName("SunJCE");
// 设置加盐的类名
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
// 设置初始向量IV生成器的类名
config.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator");
// 设置字节数组以base64格式输出
config.setStringOutputType("base64");
// 将对应的配置设置到密码器上
encryptor.setConfig(config);
}
/**
依次解析每个属性的值。判断是否是加密字段,如果是则进行解密
解密函数可以用 jsypt 自带的解密方式,也可以用自己写的一套加解密规则
*/
@Override
public String resolvePropertyValue(String value) {
System.out.println("resolvePropertyValue : " + value);
if (!StringUtils.isEmpty(value) && value.startsWith(prefix)) {
String encValue = value.substring(prefix.length());
String rawValue = encryptor.decrypt(value.substring(prefix.length()));
return rawValue;
}
return value;
}
}
}
从结果可以看出,调用 resolvePropertyValue 函数之后,会对返回值和参数进行比较,如果两者不同还会再调用一次 resolvePropertyValue ,也就是说,加密支持递归加密。