我们一般都写在yml或者properties文件中。
有没有一种办法,在配置文件中写密文,程序启动后自动解密,再使用这个解密后的密码进行连数据库或者redis?
jasypt就实现了这个功能。
https://github.com/ulisesbocchio/jasypt-spring-boot
这是jasypt的地址,上面有详细的使用说明和例子。目前版本已经更新到3.0.2
参照说明,我们看怎么使用。
目录
1,引入依赖
2,yml文件配置jasypt属性
3,用jasypt加密,在yml填写加密后的密码
com.github.ulisesbocchio
jasypt-spring-boot-starter
3.0.2
2种方式引入依赖包。
第一种是你的springboot应用使用了@SpringBootApplication or @EnableAutoConfiguration注解就可以这样引入。
如果没有使用上面的注解,就用第二种方式。并且还需要在你的启动类上加注解:
@Configuration
@EnableEncryptableProperties
package com.example.gate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
//使用jasypt的第二种方式.如果你没用到@SpringBootApplication 或 @EnableAutoConfiguration就必须用下面2个注解,才能正常使用jasypt
//@Configuration
//@EnableEncryptableProperties
//@EnableCircuitBreaker
@ComponentScan(basePackages = "com.example")
@EnableDiscoveryClient
@SpringBootApplication
public class GateApplication {
public static void main(String[] args) {
SpringApplication.run(GateApplication.class, args);
}
}
其实还有更高级的用法,可以指定哪几个配置文件用jasypt。
//使用jasypt的第二种方式.如果你没用到@SpringBootApplication 或 @EnableAutoConfiguration就必须用下面2个注解,才能正常使用jasypt
//@Configuration
//@EnableEncryptableProperties
//具体指定配置文件的用法,其他配置文件不受jasypt影响
//@Configuration
//@EncryptablePropertySources({@EncryptablePropertySource("classpath:encrypted.properties"),
// @EncryptablePropertySource("classpath:encrypted2.properties")})
//@EnableCircuitBreaker
@ComponentScan(basePackages = "com.example")
@EnableDiscoveryClient
@SpringBootApplication
public class GateApplication {
public static void main(String[] args) {
SpringApplication.run(GateApplication.class, args);
}
}
这个看你具体需要了。
#jasypt加密配置
jasypt:
encryptor:
password: miyao
algorithm: PBEWITHHMACSHA512ANDAES_256
# property:
# prefix: "ENC@["
# suffix: "]"
这是我给的一个配置。
其中秘钥password是必须自己定义的。其他都可以不配置,因为有默认的配置:
Key | Required | Default Value |
jasypt.encryptor.password | True | - |
jasypt.encryptor.algorithm | False | PBEWITHHMACSHA512ANDAES_256 |
jasypt.encryptor.key-obtention-iterations | False | 1000 |
jasypt.encryptor.pool-size | False | 1 |
jasypt.encryptor.provider-name | False | SunJCE |
jasypt.encryptor.provider-class-name | False | null |
jasypt.encryptor.salt-generator-classname | False | org.jasypt.salt.RandomSaltGenerator |
jasypt.encryptor.iv-generator-classname | False | org.jasypt.iv.RandomIvGenerator |
jasypt.encryptor.string-output-type | False | base64 |
jasypt.encryptor.proxy-property-sources | False | false |
jasypt.encryptor.skip-property-sources | False | empty list |
先说填写密码的格式:
#jasypt加密后的密码
mypass:
pass1: ENC(NfA+LtBfc26xLiHLb0EGXeNfU9TaE2tQIt7X94DrodPcUKV/tnTKQLz7bcLSM3i0)
面必须用ENC()包起来,这样jasypt才能识别出来这个密码需要解密再传给应用程序。
这个密码怎么获取呢?
方式有很多,
方法一:写个工具类,把jasypt加密的过程写出来,执行后得到密码。
方法二:写个controller,注入jasypt的加密对象,执行加密,把密码打印或者返回到页面
方法三:jasypt提供了加密的命令,
https://github.com/ulisesbocchio/jasypt-spring-boot
具体看上面的链接。这个其实意义不大。
我自己把前两种方式的代码贴出来:
工具类:
package com.example.gate.util;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
/**
* Jasypt用于在配置文件中写加密后的密码,只要配上秘钥,读入系统中的就是解密后的正确密码。
* 这个工具类主要是用于实验Jasypt的加密和解密.
* 把yml中对Jasypt的配置写到代码里而已。
*
* 实际使用Jasypt时,只需pom引入依赖,yml中配置相关项(秘钥等),然后把加密后的密码写入你需要配置的地方(yml文件。。。)
* 程序启动后,会自动解密(如果程序不能正常解密,那你的系统启动就有问题了,比如数据库连不上,redis连不上等,都是密码不正确的错)
* 我们也可以写个controller把yml配置文件中的密码打印出来,这个打印的肯定不是你写的加密的字符串而是解密后的正确密码。
* 整个解密的过程是交给Jasypt做的。加密的过程是我们提前加密得到密文,写到yml配置文件中的。但是必须有ENC()这个标识。
*
*
* @author lsy
*
*/
public class JasyptUtil {
public static void main(final String[] args) {
String miyao = "miyao";// 秘钥字符串
String pass = "123456";// 待加密的明文密码
try {
StringEncryptor stringEncryptor = JasyptUtil.getInstance(miyao);
String mima = stringEncryptor.encrypt(pass);
System.out.println("【" + pass + "】被加密成【" + mima + "】");
String jiemi = stringEncryptor.decrypt(mima);
System.out.println("【" + mima + "】被解密成【" + jiemi + "】");
} catch (Exception e) {
e.printStackTrace();
}
}
private static StringEncryptor stringEncryptor = null;//org.jasypt.encryption.StringEncryptor对象
public static StringEncryptor getInstance(String miyao) throws Exception {
if(miyao==null||miyao.trim().equals("")) {
System.out.println("秘钥不能为空!");
throw new Exception("org.jasypt.encryption.StringEncryptor秘钥不能为空!");
}
if (stringEncryptor == null) {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword("miyao");// 这个秘钥必须是我们自己定义
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
stringEncryptor = encryptor;
}
return stringEncryptor;
}
}
执行main方法打印:
【123456】被加密成【AAM7i7RkQeSDOf1iiAbYIDlfAGbJNch3jVu9KNqPLlwlHS4LFO3Sx22bTipeay5h】
【AAM7i7RkQeSDOf1iiAbYIDlfAGbJNch3jVu9KNqPLlwlHS4LFO3Sx22bTipeay5h】被解密成【123456】
注意,一个同样的密码和秘钥,每次执行加密,密文都是不一样的。但是解密是没问题的。
controller执行:
package com.example.gate.controller;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* jasypt测试
*
* @author lsy
*
*/
@RestController
@RequestMapping("/jasypt")
public class PrintPassInYmlController {
@Value("${spring.cloud.client.ip-address}")
private String ip;
@Value("${spring.application.name}")
private String servername;
@Value("${server.port}")
private String port;
@Autowired
StringEncryptor stringEncryptor;
@Value("${mypass.pass1}")
private String pass1;//yml中定义的是密文
//查看程序跑起来后,yml中的密码是否是明文
@RequestMapping(value = "/viewPass1",method = RequestMethod.GET)
@ResponseBody
public String viewPass1(){
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateStr=LocalDateTime.now().format(df);
String mess="viewPass ! 查看mypass.pass1的值是["+pass1+"]。response form ["+servername+":"+ip+":"+port+"]"+"..."+dateStr;
return mess;
}
/**
* 用jasypt把一个密码加密(秘钥用yml中定义的)
* @param param
* @return
*/
@RequestMapping(value = "/jasyptEncode",method = RequestMethod.GET)
@ResponseBody
public String jasyptEncode(@RequestParam(required=false) String param){
System.out.println("jasyptEncode接收请求参数为==="+param);
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateStr=LocalDateTime.now().format(df);
String newpass=stringEncryptor.encrypt(param);
String mess="jasyptEncode ! 把["+param+"] 加密成["+newpass+"]。response form ["+servername+":"+ip+":"+port+"]"+"..."+dateStr;
return mess;
}
/**
* 用jasypt把一个密文解密,参数是密文,返回解密后的明文(使用的秘钥还是yml中定义的)
*
* 使用post请求是因为密码有特殊字符,所以用post请求体传值
*
* @param param
* @return
*/
@RequestMapping(value = "/jasyptDecode",method = RequestMethod.POST)
@ResponseBody
public String jasyptDecode(@RequestBody String param){
System.out.println("jasyptDecode接收请求参数为==="+param);
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateStr=LocalDateTime.now().format(df);
Map hm=new HashMap();
hm.put("uname", "lsy");
String realpass=stringEncryptor.decrypt(param);
hm.put("upass", realpass);
String mess="jasyptDecode ! 把["+param+"] 解密成["+realpass+"]。response form ["+servername+":"+ip+":"+port+"]"+"..."+dateStr;
System.out.println(mess);
return "jasyptDecode response==="+hm.toString();
}
}
应用跑起来后,查看yml中mypass.pass1的值:
我加密一个密码:
为了验证解密,我把上面的密文再发到解密的请求中:
注意header的Content-Type要用text/plain
这几种方式都能获取密文,然后我们就配置到yml中就可以了。
另外说一下ENC()这个标识是可以改的,比如我先改成ENC@[],只要按以下设置即可。其他的基本上就用默认的就行。
#jasypt加密配置
jasypt:
encryptor:
password: miyao
algorithm: PBEWITHHMACSHA512ANDAES_256
property:
prefix: "ENC@["
suffix: "]"
另一个需要关心的就是秘钥的存放了。秘钥是个字符串,写在配置文件或者写在代码都可以。