我們一般都寫在yml或者properties文件中。
有沒有一種辦法,在配置文件中寫密文,程序啓動後自動解密,再使用這個解密後的密碼進行連數據庫或者redis?
jasypt就實現了這個功能。
https://github.com/ulisesbocchio/jasypt-spring-boot
這是jasypt的地址,上面有詳細的使用說明和例子。目前版本已經更新到3.0.2
參照說明,我們看怎麼使用。
目錄
1,引入依賴
<!-- jasypt方式一 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
<!-- jasypt方式二 -->
<!-- <dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot</artifactId>
<version>3.0.2</version>
</dependency> -->
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);
}
}
這個看你具體需要了。
2,yml文件配置jasypt屬性
#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 |
3,用jasypt加密,在yml填寫加密後的密碼
先說填寫密碼的格式:
#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: "]"
另一個需要關心的就是祕鑰的存放了。祕鑰是個字符串,寫在配置文件或者寫在代碼都可以。