如果你的application.properties中還存在明文密碼----加密Spring Boo

1 概述

什麼?都2020年了還在Spring Boot的配置文件中寫明文密碼?
雖然是小項目,明文也沒人看.
明文簡單快捷方便啊!!!
你看直接用戶名root密碼123456多麼簡單!!!

...
不廢話了,這篇文章主要講了如何使用jasypt-spring-boot這個開源組件來進行配置文件的加密,包括簡單加密以及非對稱加密,同時也介紹了使用jar/war部署時如何輸入加密口令.

2 簡單加密

jasypt簡單加密就是直接把加密口令寫死在文件中.(好吧這樣就差不多大概跟沒加密一樣... )

2.1 依賴

目前最新版本爲3.0.2,具體請查看官方github(戳這裏).

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

2.2 加密口令

在application.properties中加上:

jasypt.encryptor.password=xxx

xxx爲對稱加密的口令.
默認使用PBE算法進行加密,PBE其實並沒有包含真正的加密與解密算法,而是將已有的消息摘要算法(如MD5,SHA等)與對稱加密算法(如AES,DES,RC2等)進行了組合,默認組合的是HCMA消息認證算法,SHA512消息摘要算法以及AES256對稱加密算法.PBE使用口令與隨機生成的鹽去生成對應的對稱加密密鑰,再用密鑰去進行對稱加密.

2.3 輸出密文

這裏在配置文件中加一個測試字段password與密鑰test進行測試:
在這裏插入圖片描述
這裏爲了方便就在run裏面測試:

@SpringBootApplication
@EnableEncryptableProperties
public class DemoApplication implements CommandLineRunner {
    private static final Logger l = LoggerFactory.getLogger(DemoApplication.class);

    @Autowired
    private StringEncryptor stringEncryptor;

    @Autowired
    private ApplicationContext applicationContext;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        Environment environment = applicationContext.getEnvironment();
        l.info(stringEncryptor.encrypt(environment.getProperty("password")));
    }
}

注意使用@Autowired進行StringEncryptor的自動裝配時,官方文檔說加上

@Configuration
@EnableEncryptableProperties

在這裏插入圖片描述
由於

@SpringBootApplication

包含了

@Configuration

因此這裏只需要後一個.
運行後獲取密文輸出:
在這裏插入圖片描述

2.4 替換配置文件

把上面的密文替換到原配置文件,加上前綴ENC(與後綴):
在這裏插入圖片描述
這樣就加密成功了,直接獲取屬性可以看到明文:
在這裏插入圖片描述

3 自定義加密

當然,上面的簡單加密不能滿足實際使用需求,因此,這裏需要進行自定義加密.

3.1 自定義加密前後綴

需要使用一個前後綴區分需要加密與不需加密的字段,默認前綴爲

ENC(

後綴爲:

)

因此加密時需要加上ENC(與).
自定義前後綴指定兩個屬性就可以了:
在這裏插入圖片描述
密碼字段需要對應修改.

3.2 口令參數化

其實就是在啓動的時候加上命令行參數或者應用環境變量,或者通過系統環境變量讀取口令,詳細使用方式請看第4點部署.
命令行參數:

java -jar xxx.jar --jasypt.encryptor.password=xxx

應用環境變量:

java -Djasypt.encryptor.password=xxx -jar xxx.jar

系統環境變量:

jasypt.encryptor.password=${TEST}

前提是已經設置好對應系統變量.

3.3 自定義加密類

可以實現StringEncryptor接口,重寫裏面的encrypt與decrypt方法,再定義一個加密配置類,指定加密類的名字:

@Configuration
@EnableEncryptableProperties
public class MyEncryptorConfiguration {
    @Bean("MyEncryptor")
    public StringEncryptor getStringEncryptor()
    {
        return new StringEncryptor() {
            @Override
            public String encrypt(String s) {
                return "111";
            }

            @Override
            public String decrypt(String s) {
                return "222";
            }
        };
    }
}

這裏是一個很簡單的例子,加密直接返回111,解密直接返回222,具體加解密算法直接替換函數體即可.
注意需要在配置文件中寫上Bean的名字:

jasypt.encryptor.bean=codeSheepEncryptorBean

使用構造函數注入(Autowired也可以):

private final StringEncryptor stringEncryptor;
public DemoApplication(MyEncryptorConfiguration encryptorConfiguration)
{
    stringEncryptor = encryptorConfiguration.getStringEncryptor();
}

測試:

@Override
public void run(String... args) throws Exception {
    Environment environment = applicationContext.getEnvironment();
    l.info(stringEncryptor.encrypt(environment.getProperty("password")));
    l.info(stringEncryptor.decrypt(environment.getProperty("password")));
}

在這裏插入圖片描述

4 部署

4.1 jar部署

4.1.1 命令行參數方式

這種方式的話先把配置文件中的jasypt.encryptor.password去掉,然後修改在Spring Boot的運行配置,進行本地測試:
在這裏插入圖片描述
打包時,如果測試的話需要設置Maven的參數,不測試的話直接勾選Skip Tests:
在這裏插入圖片描述
打包後(右側Maven->package)加上參數運行就可以了:
在這裏插入圖片描述

4.1.2 應用環境變量方式

其實和第一種方式差不多,也是把jasypt.encryptor.password去掉,在VM options中設置參數,Spring Boot運行配置如下:
在這裏插入圖片描述
Maven設置(當然也可以跳過測試):
在這裏插入圖片描述
不過遺憾的是筆者測試失敗了:
在這裏插入圖片描述
沒理由啊,那爲什麼Spring Boot那裏就這樣設置就可以....
(有大佬知道爲什麼會失敗的話可以留言,感激不盡.)
這裏就直接跳過測試了.
在這裏插入圖片描述
然後就可以愉快地運行了(筆者的win下需要加兩個單引號):
在這裏插入圖片描述

4.1.3 系統環境變量方式

設置環境變量這個應該不用怎麼說了,直接去設置就行,然後修改一下jasypt.encryptor.password,兩個花括號中間是對應的環境變量名:
在這裏插入圖片描述
Spring Boot運行配置:
在這裏插入圖片描述
Maven:
在這裏插入圖片描述
這次Maven測試就沒問題了.
真是奇了怪了.
運行(還是這個舒服,直接-jar):
在這裏插入圖片描述

4.2 war部署

4.2.1 jar-war轉換

原來的是jar打包,換成war時,需要修改pom.xml中的<packaging>爲war,同時加上tomcat依賴:

<packaging>war</packaging>
...
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

再添加一個ServletInitializer:

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
    {
        return builder.sources(DemoApplication.class);
    }
}

其中DemoApplication爲main函數所在的類.
war轉爲jar時進行對應的相反操作就可以了.

4.2.2 命令行參數方式

Maven設置就不說了,像上面一樣,打包之後...
筆者找不到設置Tomcat命令行參數的方式,所以,就跳過這個了...
(歡迎大佬找到的留言補充,感激不盡!!!)
筆者太菜了,害.

4.2.3 應用環境變量方式

win下可以直接修改catalina.bat或者進入tomcat9w.exe(tomcat9,tomcat8是tomcat8w.exe)進行圖形化修改,這裏選擇修改catalina.bat的方式,找到setlocal,後面加上

set "JAVA_OPTS=-Djasypt.encryptor.password=test"

在這裏插入圖片描述
然後把war放到webapps下就可以了.

4.2.4 環境變量方式

這種方式最簡單,設置好了環境變量,修改配置文件:
在這裏插入圖片描述
直接war打包部署就行.

5 非對稱加密

Spring Boot2.2.1之後支持非對稱加密,密鑰對的格式可以爲PEM/DER.

5.1 加密

這裏使用的一位大佬的RSA自定義位數加密工具類(戳這裏),無需額外依賴,僅自帶JDK實現(JDK8+).

import java.util.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
 * Java RSA 加密工具類
 * 參考: https://blog.csdn.net/qy20115549/article/details/83105736
 */
public class Test {
    /**
     * 密鑰長度 於原文長度對應 以及越長速度越慢
     */
    private final static int KEY_SIZE = 2048;
    /**
     * 用於封裝隨機產生的公鑰與私鑰
     */
    private static Map<Integer, String> keyMap = new HashMap<Integer, String>();
    /**
     * 隨機生成密鑰對
     */
    public static void genKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator類用於生成公鑰和私鑰對,基於RSA算法生成對象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密鑰對生成器
        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
        // 生成一個密鑰對,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 得到私鑰
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到公鑰
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        // 得到私鑰字符串
        String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
        // 將公鑰和私鑰保存到Map
        //0表示公鑰
        keyMap.put(0, publicKeyString);
        //1表示私鑰
        keyMap.put(1, privateKeyString);
    }
    /**
     * RSA公鑰加密
     *
     * @param str       加密字符串
     * @param publicKey 公鑰
     * @return 密文
     * @throws Exception 加密過程中的異常信息
     */
    public static String encrypt(String str, String publicKey) throws Exception {
        //base64編碼的公鑰
        byte[] decoded = Base64.getDecoder().decode(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }
    /**
     * RSA私鑰解密
     *
     * @param str        加密字符串
     * @param privateKey 私鑰
     * @return 明文
     * @throws Exception 解密過程中的異常信息
     */
    public static String decrypt(String str, String privateKey) throws Exception {
        //64位解碼加密後的字符串
        byte[] inputByte = Base64.getDecoder().decode(str);
        //base64編碼的私鑰
        byte[] decoded = Base64.getDecoder().decode(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }

    public static void main(String[] args) throws Exception {
        long temp = System.currentTimeMillis();
        //生成公鑰和私鑰
        genKeyPair();
        //加密字符串
        System.out.println("公鑰:" + keyMap.get(0));
        System.out.println("私鑰:" + keyMap.get(1));
        System.out.println("生成密鑰消耗時間:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
        // String message = "RSA測試ABCD~!@#$";
        String message = "test";
        System.out.println("原文:" + message);
        temp = System.currentTimeMillis();
        String messageEn = encrypt(message, keyMap.get(0));
        System.out.println("密文:" + messageEn);
        System.out.println("加密消耗時間:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
        temp = System.currentTimeMillis();
        String messageDe = decrypt(messageEn, keyMap.get(1));
        System.out.println("解密:" + messageDe);
        System.out.println("解密消耗時間:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
    }
}

5.2 修改配置文件

把明文輸入,得到密文與私鑰後,替換原來的配置文件:
在這裏插入圖片描述
密文複製到對應加密字段,加上前後綴,同時私鑰格式選擇der,把私鑰複製過去:
在這裏插入圖片描述
運行測試沒問題就可以了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章