Home  >  Article  >  Java  >  How Springboot implements encryption of plaintext passwords in configuration files

How Springboot implements encryption of plaintext passwords in configuration files

WBOY
WBOYforward
2023-05-10 22:25:111411browse

Example display

Let’s take a look at this configuration:

spring:
  # 数据库链接配置
  datasource:
    url: jdbc:mysql://xx.xx.xx.xx:3306/database
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: "123456"

The corresponding value of our above configuration spring.datasource.password is 123456, it is inappropriate to put such sensitive information directly in the configuration file. What we have to do is to change the corresponding value to an encrypted ciphertext, as follows:

spring:
  # 数据库链接配置
  datasource:
    url: jdbc:mysql://xx.xx.xx.xx:3306/database
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: "AES(DzANBAhBWXxZqAOsagIBCoaw8FV4gYRbid7G70UEM24=)"

In this case, even if the configuration file is intentionally If someone takes it, they don’t know what the real database password is, so they cannot pose a risk of infringement to the project;

Principle Analysis

In order to implement this function, we need to understand The relevant extension points of Spring and the corresponding data encryption and decryption knowledge, let’s first take a look at which extension point of Spring we should cut through;

We want to intercept the configuration data If so, it can be handled by implementing a custom BeanFactoryPostProcessor:

public class PropertySourcePostProcessor implements BeanFactoryPostProcessor {

  private ConfigurableEnvironment environment;

  public PropertySourcePostProcessor(ConfigurableEnvironment environment) {
    this.environment = environment;
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    // 从ConfigurableEnvironment中取出所有的配置数据
    MutablePropertySources propertySources = this.environment.getPropertySources();
    propertySources.stream()
        // 过滤不需要包装的对象
        .filter(s -> !noWrapPropertySource(s))
        // 包装所有的PropertySource
        .map(s -> new EncryPropertySource(s))
        .collect(Collectors.toList())
        // 替换掉propertySources中的PropertySource
        .forEach(wrap -> propertySources.replace(wrap.getName(), wrap));
  }

  private boolean noWrapPropertySource(PropertySource propertySource) {
    return propertySource instanceof EncryPropertySource || StringUtils.equalsAny(propertySource.getClass().getName(), "org.springframework.core.env.PropertySource$StubPropertySource", "org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource");
  }
}

The basic principle is analyzed as follows:

1. Take out all # through ConfigurableEnvironment ##PropertySource and traverse in sequence;

2. Filter out

PropertySource that does not meet our requirements, because PropertySource has many subclasses, not all of them PropertySource instances all meet our packaging requirements;

3. Make a layer of packaging for

PropertySource that meets the requirements, which is actually a static proxy;

4. Replace the previous

PropertySource instance with the packaged PropertySource;

Through the above series of operations, we can

PropertySource When getting the value, do some custom operations, such as decrypting the ciphertext password;

The other remaining problem is the encryption and decryption problem. There are symmetric encryption and asymmetric encryption in cryptography. This The difference between the two encryption methods is that symmetric encryption requires the same key for encryption and decryption, while asymmetric encryption requires a public key for encryption and a private key for decryption;

Understand symmetric encryption and asymmetric encryption The difference in encryption, if we are using symmetric encryption, we must avoid putting the ciphertext and the key in the same place;

Asymmetric encryptionWe must avoid putting the ciphertext and the private key in the same place;

Tool introduction

Next we will introduce a

jar tool specifically for this need, it is jasypt, we can go to maven Find the relevant package in the warehouse:

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

Its implementation principle is actually what we described above, by customizing

BeanFactoryPostProcessor to ConfigurableEnvironment PropertySourceThe instance is intercepted and packaged, and a layer of decryption operation is performed on the implementation of the packaging class, thus realizing the decryption of the ciphertext password;

After importing the above dependencies, the tool has It takes effect automatically, and we can modify the corresponding configuration. First, we first make some configurations for the tool:

jasypt:
  encryptor:
    # 密钥
    password: ""
    property:
      # 密文前缀
      prefix: ""
      # 密文后缀
      suffix: ""

In the above configuration,

jasypt.encryptor.password must be configured Yes, this is the encryption and decryption key. The default encryption algorithm is PBEWITHHMACSHA512ANDAES_256; in addition, jasypt.encryptor.property.prefix and jasypt.encryptor.property.suffix are the ciphertext prefix and ciphertext suffix respectively, which are used to mark the ciphertext that needs to be decrypted. If not configured, the default ciphertext prefix is ​​ENC(, the ciphertext suffix is ​​); By default, our ciphertext is as follows:

spring:
  datasource:
    password: "ENC(DzANBAhBWXxZqAOsagIBCoaw8FV4gYRbid7G70UEM24=)"

Another point to note is that

jasypt.encryptor.password cannot be placed together with the ciphertext, we It can be passed through system properties, command line parameters or environment variables in the project;

Implement custom encryption and decryption

If the encryption and decryption method provided by

jasypt cannot meet our needs For project requirements, we can also implement encryption and decryption ourselves:

@Bean("jasyptStringEncryptor")
  public StringEncryptor jasyptStringEncryptor(){
    return new StringEncryptor() {
      @Override
      public String encrypt(String s) {
        // TODO 加密
        return null;
      }

      @Override
      public String decrypt(String s) {
        // TODO 解密
        return null;
      }
    };
  }

Note that our

BeanName must be set to jasyptStringEncryptor by default, otherwise it will not take effect. If If you want to change this BeanName, you can also customize the BeanName corresponding to the StringEncryptor instance by modifying this configuration parameter:

jasypt:
  encryptor:
    # 自定义StringEncryptor的BeanName
    bean: ""

How to generate the password Text

The operation of generating ciphertext still requires encrypting and generating it by calling the

StringEncryptor instance. You can refer to the following code:

@Component
public class StringEncryptorUtil{
  @Autowired
  private StringEncryptor encryptor;
  
  public void encrypt(){
    String result = encryptor.encrypt("123456");
    System.out.println(result);
  }
}

After all, the operation that requires encryption only requires It is executed once in the project life cycle, so we only need to simply write a tool class and call it.

The above is the detailed content of How Springboot implements encryption of plaintext passwords in configuration files. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete