preface

In our daily development, we can easily expose the database password in plain text in the configuration file. In the development environment, we can do this, but in the production environment, it is not recommended. After all, there is no small matter of security, no one knows which day the password will be disclosed for no reason. How to encrypt database passwords in springBoot

The body of the

Druid database connection pool to encrypt database passwords

Pom.xml introduces the Druid package

To facilitate other operations, druid’s starter is introduced directly here

<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>${druid.version}</version>
		</dependency>
Copy the code

2, the use of com. Alibaba. Druid. Filter. Config. ConfigTools generate public and private key

Ps: There are two ways of generation, one is to use the command line generation, the other is to write a tool class generation directly. The examples in this article are generated directly from the utility classes

The code for the utility class is as follows

/** * alibaba druid encryption and decryption rules: * plaintext password + privateKey (privateKey) encryption = encryption password * encryption password + publicKey (publicKey) decryption = plaintext password */
public final class DruidEncryptorUtils {

    private static String privateKey;

    private static String publicKey;

    static {
        try {
            String[] keyPair = ConfigTools.genKeyPair(512);
            privateKey = keyPair[0];
            System.out.println(String.format("privateKey-->%s",privateKey));
            publicKey = keyPair[1];
            System.out.println(String.format("publicKey-->%s",publicKey));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch(NoSuchProviderException e) { e.printStackTrace(); }}/** * plaintext encryption *@param plaintext
     * @return* /
    @SneakyThrows
    public static String encode(String plaintext){
        System.out.println("Plaintext string:" + plaintext);
        String ciphertext = ConfigTools.encrypt(privateKey,plaintext);
        System.out.println("Encrypted string:" + ciphertext);
        return ciphertext;
    }

    /** * decrypt *@param ciphertext
     * @return* /
    @SneakyThrows
    public static String decode(String ciphertext){
        System.out.println("Encrypted string:" + ciphertext);
        String plaintext = ConfigTools.decrypt(publicKey,ciphertext);
        System.out.println("Decrypted string:" + plaintext);

        return plaintext;
    }

Copy the code

3. Modify the content of the database configuration file

A. Change the password

Replace the password with the one generated with the DruidEncryptorUtils utility class

 password: ${DATASOURCE_PWD:HB5FmUeAI1U81YJrT/T6awImFg1/Az5o8imy765WkVJouOubC2H80jqmZrr8L9zWKuzS/8aGzuQ4YySAkhywnA==}
Copy the code

B. Filter Enable config

 filter:
                config:
                    enabled: true
Copy the code

C. Configure the connectionProperties property

 connection-properties: config.decrypt=true; config.decrypt.key=${spring.datasource.publickey}
Copy the code

Ps: spring. The datasource. Publickey class generation public key for the tool

Appendix: Complete database configuration

spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        url: ${DATASOURCE_URL:jdbc:mysql://localhost:3306/demo? useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai}
        username: ${DATASOURCE_USERNAME:root}
        password: ${DATASOURCE_PWD:HB5FmUeAI1U81YJrT/T6awImFg1/Az5o8imy765WkVJouOubC2H80jqmZrr8L9zWKuzS/8aGzuQ4YySAkhywnA==}
        publickey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIvP9xF4RCM4oFiu47NZY15iqNOAB9K2Ml9fiTLa05CWaXK7uFwBImR7xltZM1frl6ahWAXJB6a/FSjtJkTZUJEC AwEAAQ==
        druid:
            # initial connection number
            initialSize: 5
            Minimum number of connection pools
            minIdle: 10
            # Maximum number of connection pools
            maxActive: 20
            Set the connection wait timeout
            maxWait: 60000
            Configure how often to detect idle connections that need to be closed, in milliseconds
            timeBetweenEvictionRunsMillis: 60000
            Set the minimum time for a connection to live in the pool in milliseconds
            minEvictableIdleTimeMillis: 300000
            Set the maximum number of milliseconds for a connection to live in the pool
            maxEvictableIdleTimeMillis: 900000
            Configure to check whether the connection is valid
            validationQuery: SELECT 1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: true
                Set whitelist to allow all access
                allow:
                url-pattern: /druid/*
                The console manages user names and passwords
                login-username:
                login-password:
            filter:
                stat:
                    enabled: true
                    Slow SQL logging
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true
                config:
                    enabled: true
            connection-properties: config.decrypt=true; config.decrypt.key=${spring.datasource.publickey}

Copy the code

Scheme 2: Use JASypt to encrypt database passwords

Pom.xml is introduced into the Jasypt package

<dependency>
			<groupId>com.github.ulisesbocchio</groupId>
			<artifactId>jasypt-spring-boot-starter</artifactId>
			<version>${jasypt.verison}</version>
		</dependency>
Copy the code

2. Use the tool class provided by JASypt to encrypt plaintext passwords

The encryption tool classes are as follows

public final class JasyptEncryptorUtils {


    private static final String salt = "lybgeek";

    private static BasicTextEncryptor basicTextEncryptor = new BasicTextEncryptor();

    static {
        basicTextEncryptor.setPassword(salt);
    }

    private JasyptEncryptorUtils(a){}

    /** * plaintext encryption *@param plaintext
     * @return* /
    public static String encode(String plaintext){
        System.out.println("Plaintext string:" + plaintext);
        String ciphertext = basicTextEncryptor.encrypt(plaintext);
        System.out.println("Encrypted string:" + ciphertext);
        return ciphertext;
    }

    /** * decrypt *@param ciphertext
     * @return* /
    public static String decode(String ciphertext){
        System.out.println("Encrypted string:" + ciphertext);
        ciphertext = "ENC(" + ciphertext + ")";
        if (PropertyValueEncryptionUtils.isEncryptedValue(ciphertext)){
            String plaintext = PropertyValueEncryptionUtils.decrypt(ciphertext,basicTextEncryptor);
            System.out.println("Decrypted string:" + plaintext);
            return plaintext;
        }
        System.out.println("Decryption failed");
        return ""; }}Copy the code

3. Modify the content of the database configuration file

A. Wrap the encrypted string generated by JasyptEncryptorUtils with ENC

password: ${DATASOURCE_PWD:ENC(P8m43qmzqN4c07DCTPey4Q==)}
Copy the code

B. Configure the key and specify the encryption and decryption algorithm

jasypt:
    encryptor:
        password: lybgeek
        algorithm: PBEWithMD5AndDES
        iv-generator-classname: org.jasypt.iv.NoIvGenerator
Copy the code

Because I tools use encryption tool class is BasicTextEncryptor, the corresponding configuration of decryption is PBEWithMD5AndDES and org. Jasypt. Iv. NoIvGenerator

Ps: In the production environment, you are advised to configure keys in the following way to prevent key leakage

java -jar -Djasypt.encryptor.password=lybgeek
Copy the code

Appendix: Complete database configuration

spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: ${DATASOURCE_URL:ENC(kT/gwazwzaFNEp7OCbsgCQN7PHRohaTKJNdGVgLsW2cH67zqBVEq7mN0BTIXAeF4/Fvv4l7myLFx0y6ap4umod7C2VWgyRU5UQt KmdwzQN3hxVxktIkrFPn9DM6+YahM0xP+ppO9HaWqA2ral0ejBCvmor3WScJNHCAhI9kHjYc=)} username: ${DATASOURCE_USERNAME:ENC(rEQLlqM5nphqnsuPj3MlJw==)} password: ${DATASOURCE_PWD:ENC(P8m43qmzqN4c07DCTPey4Q==)} druid: # initialSize:5MinIdle:10MaxActive:20MaxWait:60000# configuration interval how to conduct a test, testing needs to be closed free connection, unit is a millisecond timeBetweenEvictionRunsMillis:60000# to configure a connection in the pool minimum survival time, the unit is a millisecond minEvictableIdleTimeMillis:300000# to configure a connection in the pool the largest survival time, the unit is a millisecond maxEvictableIdleTimeMillis:900000ValidationQuery: SELECT1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: trueAllow: url-pattern: /druid allow: url-pattern: /druid/* # login-username: login-password: filter: stat: enabled: true # log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true jasypt: encryptor: password: lybgeek algorithm: PBEWithMD5AndDES iv-generator-classname: org.jasypt.iv.NoIvGeneratorCopy the code

Plan 3: Custom implementation

Implementation principle: Use spring backend processor to modify DataSource

1, custom encryption and decryption tool class

/** * Using hutool encapsulation encryption and decryption tool, using AES symmetric encryption algorithm as an example */
public final class EncryptorUtils {

    private static String secretKey;



    static {
        secretKey = Hex.encodeHexString(SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded());
        System.out.println("secretKey-->" + secretKey);
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
    }

    /** * plaintext encryption *@param plaintext
     * @return* /
    @SneakyThrows
    public static String encode(String plaintext){
        System.out.println("Plaintext string:" + plaintext);
        byte[] key = Hex.decodeHex(secretKey.toCharArray());
        String ciphertext =  SecureUtil.aes(key).encryptHex(plaintext);
        System.out.println("Encrypted string:" + ciphertext);

        return ciphertext;
    }

    /** * decrypt *@param ciphertext
     * @return* /
    @SneakyThrows
    public static String decode(String ciphertext){
        System.out.println("Encrypted string:" + ciphertext);
        byte[] key = Hex.decodeHex(secretKey.toCharArray());
        String plaintext = SecureUtil.aes(key).decryptStr(ciphertext);
        System.out.println("Decrypted string:" + plaintext);

        return plaintext;
    }

    /** * plaintext encryption *@param plaintext
     * @return* /
    @SneakyThrows
    public static String encode(String secretKey,String plaintext){
        System.out.println("Plaintext string:" + plaintext);
        byte[] key = Hex.decodeHex(secretKey.toCharArray());
        String ciphertext =  SecureUtil.aes(key).encryptHex(plaintext);
        System.out.println("Encrypted string:" + ciphertext);

        return ciphertext;
    }

    /** * decrypt *@param ciphertext
     * @return* /
    @SneakyThrows
    public static String decode(String secretKey,String ciphertext){
        System.out.println("Encrypted string:" + ciphertext);
        byte[] key = Hex.decodeHex(secretKey.toCharArray());
        String plaintext = SecureUtil.aes(key).decryptStr(ciphertext);
        System.out.println("Decrypted string:" + plaintext);

        returnplaintext; }}Copy the code

2. Write a post-processor

public class DruidDataSourceEncyptBeanPostProcessor implements BeanPostProcessor {

    private CustomEncryptProperties customEncryptProperties;

    private DataSourceProperties dataSourceProperties;

    public DruidDataSourceEncyptBeanPostProcessor(CustomEncryptProperties customEncryptProperties, DataSourceProperties dataSourceProperties) {
        this.customEncryptProperties = customEncryptProperties;
        this.dataSourceProperties = dataSourceProperties;
    }



    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof DruidDataSource){
            if(customEncryptProperties.isEnabled()){
                DruidDataSource druidDataSource = (DruidDataSource)bean;
                System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
                String username = dataSourceProperties.getUsername();
                druidDataSource.setUsername(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),username));
                System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
                String password = dataSourceProperties.getPassword();
                druidDataSource.setPassword(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),password));
                System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
                String url = dataSourceProperties.getUrl();
                druidDataSource.setUrl(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),url));
                System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -"); }}returnbean; }}Copy the code

3. Modify the content of the database configuration file

A. Change the password

Replace the password with the encrypted password generated with the custom encryption utility class

  password: ${DATASOURCE_PWD:fb31cdd78a5fa2c43f530b849f1135e7}
Copy the code

B. Specify the key and enable encryption

custom:
    encrypt:
        enabled: true
        secret-key: 2f8ba810011e0973728afa3f28a0ecb6
Copy the code

Ps: Secret-key is also not directly exposed in the configuration file. You can specify it by using -dcustom.encrypt. secret-key

Appendix: Complete database configuration

spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: ${DATASOURCE_URL:dcb268cf3a2626381d2bc5c96f94fb3d7f99352e0e392362cb818a321b0ca61f3a8dad3aeb084242b745c61a1d3dc244ed1484b f745c858c44560dde10e60e90ac65f77ce2926676df7af6b35aefd2bb984ff9a868f1f9052ee9cae5572fa015b66a602f32df39fb1bbc36e04cc0f14 8e4d610a3e5d54f2eb7c57e4729c9d7b4} username: ${DATASOURCE_USERNAME:61db3bf3c6d3fe3ce87549c1af1e9061} password: # ${DATASOURCE_PWD: fb31cdd78a5fa2c43f530b849f1135e7} druid: the initial number of connections initialSize:5MinIdle:10MaxActive:20MaxWait:60000# configuration interval how to conduct a test, testing needs to be closed free connection, unit is a millisecond timeBetweenEvictionRunsMillis:60000# to configure a connection in the pool minimum survival time, the unit is a millisecond minEvictableIdleTimeMillis:300000# to configure a connection in the pool the largest survival time, the unit is a millisecond maxEvictableIdleTimeMillis:900000ValidationQuery: SELECT1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: trueAllow: url-pattern: /druid allow: url-pattern: /druid/* # login-username: login-password: filter: stat: enabled: true # log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true custom: encrypt: enabled: true secret-key: 2f8ba810011e0973728afa3f28a0ecb6Copy the code

conclusion

Personally, I recommend jasypt, because it can encrypt not only passwords but also other contents. Druid can only encrypt database passwords. As for the custom scheme, it belongs to practice, after all, open source already have things, do not build their own wheels.

Finally, if jasypt is higher than version 2 and lower than 3.0.3, it will cause the dynamic refresh configuration of configuration centers such as Apollo or Nacos to fail (the latest version of 3.0.3 has officially been fixed).

If a configuration center is used, jasypt recommends using version 3 or lower, or 3.0.3

The demo link

Github.com/lyb-geek/sp…