Heim  >  Artikel  >  Java  >  Wie Springboot die Verschlüsselung von Klartext-Passwörtern in Konfigurationsdateien implementiert

Wie Springboot die Verschlüsselung von Klartext-Passwörtern in Konfigurationsdateien implementiert

WBOY
WBOYnach vorne
2023-05-10 22:25:111457Durchsuche

Beispielanzeige

Sehen wir uns diese Konfiguration an:

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

Der entsprechende Wert unserer obigen Konfiguration spring.datasource.password ist 123456. Solche sensiblen Informationen können sein Direkt platziert Es ist sehr unangemessen, den entsprechenden Wert wie folgt in einen verschlüsselten Chiffretext zu ändern: spring.datasource.password对应的值为123456,这么敏感的信息直接放在配置文件中很不合适,我们要做的就是对应的值改成一个加密的密文,如下:

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

这样的话,即使该配置文件被有心之人拿去,也不知道真正的数据库密码是啥,也就无法构成对项目的侵害风险;

原理解析

我们为了实现这个功能,需要了解Spring的相关扩展点以及对应的数据加解密知识,我们先来看看我们应该通过Spring的哪个扩展点进行切入;

我们想要拦截配置数据的话,可以通过实现自定义的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");
  }
}

基本原理解析如下:

1.通过ConfigurableEnvironment取出所有的PropertySource并依次遍历;

2.过滤掉不符合我们要求的PropertySource,因为PropertySource有很多子类,并不是所有的PropertySource实例都符合我们包装的要求;

3.对符合要求的PropertySource做一层包装,其实就是静态代理;

4.用包装好的PropertySource替换掉之前的PropertySource实例;

通过上述一系列的操作,我们就可以在PropertySource取值的时候做一些自定义的操作了,比如针对密文密码进行解密;

剩下的另一个问题就是加解密的问题,密码学里面有对称加密和非对称加密,这两种加密方式的区别就是对称加密的加密解密都需要同一个密钥,而非对称加密加密的时候需要公钥,解密的时候需要私钥;

了解了对称加密与非对称加密的区别,如果我们使用的是对称加密,那么一定要避免密文和密钥放在同一个地方;非对称加密一定要避免密文和私钥放在同一个地方;

工具介绍

接下来我们要介绍一款专门针对这个需求的jar工具,它就是jasypt,我们可以去maven仓库找到相关的包:

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

它的实现原理其实就是我们上面所讲述的,通过自定义BeanFactoryPostProcessorConfigurableEnvironment中的PropertySource实例进行拦截包装,在包装类的实现上做一层解密操作,这样就实现了对密文密码的解密;

导入上述依赖后,该工具就已经自动生效了,我们就可以修改对应的配置了,首先我们先针对该工具做一些配置:

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

在上述配置中,jasypt.encryptor.password是一定要配置的,这就是加解密的密钥,默认的加密算法是PBEWITHHMACSHA512ANDAES_256;另外jasypt.encryptor.property.prefixjasypt.encryptor.property.suffix分别是密文前缀和密文后缀,是用来标注需要解密的密文的,如果不配置,默认的密文前缀是ENC(,密文后缀是);默认情况下,我们的密文如下所示:

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

还有一个需要注意的点就是jasypt.encryptor.password不能与密文放在一起,我们可以在项目当中通过系统属性、命令行参数或环境变量传递;

实现自定义加解密

如果jasypt提供的加解密方式不能满足咱们的项目需求,我们还可以自己实现加解密:

@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;
      }
    };
  }

注意我们的BeanName,默认情况下一定要设置成jasyptStringEncryptor,否则不会生效,如果想要改变这个BeanName,也可以通过修改这个配置参数来自定义StringEncryptor实例所对应的BeanName

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

如何生成密文

生成密文的这个操作还是要自个儿通过调用StringEncryptor

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

In diesem Fall wird die Konfigurationsdatei von jemandem mit Absicht übernommen Es ist nicht bekannt, um welches echte Datenbankkennwort es sich handelt. Es stellt kein Risiko einer Verletzung des Projekts dar.

Prinzipielle Analyse: Um diese Funktion zu implementieren, müssen wir die relevanten Erweiterungspunkte von Spring und die entsprechenden Datenverschlüsselungs- und -entschlüsselungskenntnisse. Schauen wir uns zunächst an, welchen Erweiterungspunkt von <code>Spring wir durchschneiden sollten. 🎜🎜Wenn wir Konfigurationsdaten abfangen möchten, können wir damit umgehen benutzerdefinierter BeanFactoryPostProcessor: 🎜rrreee🎜 Das Grundprinzip wird wie folgt analysiert: 🎜🎜1. Nehmen Sie alle PropertySource über ConfigurableEnvironment heraus und durchlaufen Sie sie nacheinander 🎜🎜2. Filtern Sie PropertySource heraus, die unseren Anforderungscode nicht erfüllt, da PropertySource viele Unterklassen hat und nicht alle PropertySource-Instanzen unseren Anforderungen entsprechen Verpackungsanforderungen; 🎜🎜3. Für PropertySource erfüllt, erstellen Sie eine Verpackungsschicht, die eigentlich ein statischer Proxy ist 🎜🎜4 die vorherige <code>PropertySource-Instanz 🎜🎜Durch die obige Operationsreihe können wir einige benutzerdefinierte Vorgänge ausführen, wenn PropertySource den Wert annimmt, z. B. das Entschlüsseln des Chiffretext-Passworts; Ein weiteres verbleibendes Problem ist die Frage der Verschlüsselung und Entschlüsselung, der Kryptographie. Es gibt symmetrische Verschlüsselung und asymmetrische Verschlüsselung. Der Unterschied zwischen diesen beiden Verschlüsselungsmethoden besteht darin, dass die symmetrische Verschlüsselung denselben Schlüssel für die Verschlüsselung und Entschlüsselung erfordert, während die asymmetrische Verschlüsselung einen öffentlichen Schlüssel für die Verschlüsselung erfordert ein privater Schlüssel zur Entschlüsselung; 🎜🎜Verstehen Sie den Unterschied zwischen symmetrischer Verschlüsselung und asymmetrischer Verschlüsselung. Wir müssen vermeiden, den Chiffretext und den Schlüssel an derselben Stelle zu platzieren Vermeiden Sie es, den Chiffretext und den privaten Schlüssel an derselben Stelle zu platzieren. 🎜🎜Tool-Einführung🎜🎜Als nächstes stellen wir ein jar-Tool speziell für diesen Bedarf vor: code>jasypt können wir zum maven-Warehouse gehen, um das entsprechende Paket zu finden: 🎜rrreee🎜 Das Implementierungsprinzip ist tatsächlich das, was wir oben beschrieben haben, indem wir BeanFactoryPostProcessor anpassen zu ConfigurableEnvironment Die PropertySource-Instanz wird abgefangen und verpackt, und eine Ebene der Entschlüsselungsoperation wird auf der Implementierung der Verpackungsklasse ausgeführt, wodurch die Entschlüsselung des Chiffretext-Passworts realisiert wird; 🎜Nach dem Importieren der oben genannten Abhängigkeiten wird das Tool automatisch wirksam, wir können die entsprechende Konfiguration ändern. Zuerst nehmen wir einige Konfigurationen für das Tool vor: 🎜rrreee🎜In der obigen Konfiguration ist jasypt.encryptor.password muss konfiguriert werden, was Verschlüsselung und Entschlüsselung ist. Der Schlüssel, der Standardverschlüsselungsalgorithmus, ist PBEWITHHMACSHA512ANDAES_256, außerdem jasypt.encryptor.property.prefix und jasypt. encryptor.property.suffix sind jeweils Das Chiffretext-Präfix und das Chiffretext-Suffix werden verwendet, um den Chiffretext zu markieren, der entschlüsselt werden muss. Wenn nicht konfiguriert, lautet das Standard-Chiffretext-Präfix ENC(, und das Chiffretext-Suffix ist ); Standardmäßig sieht unser Chiffretext so aus: 🎜rrreee🎜 Ein weiterer zu beachtender Punkt ist, dass jasypt.encryptor.password nicht zusammengefügt werden kann mit dem Chiffretext. Wir können Systemeigenschaften, Befehlszeilenparameter oder Umgebungsvariablen übergeben; 🎜

Benutzerdefinierte Verschlüsselung und Entschlüsselung implementieren

🎜Wenn die von jasypt bereitgestellten Verschlüsselungs- und Entschlüsselungsmethoden nicht erfüllt werden können Wenn unser Projekt benötigt wird, können wir die Verschlüsselung und Entschlüsselung auch selbst implementieren: 🎜rrreee🎜Achten Sie auf unseren BeanName. Er muss standardmäßig auf jasyptStringEncryptor eingestellt sein, andernfalls wird dies nicht der Fall sein wirksam werden. Wenn Sie diesen BeanName ändern möchten, können Sie auch den BeanName entsprechend der StringEncryptor-Instanz anpassen, indem Sie diesen Konfigurationsparameter ändern: 🎜 rrreee

So generieren Sie Chiffretext

🎜Generierung Dieser Vorgang von Chiffretext muss noch verschlüsselt und durch Aufrufen der StringEncryptor-Instanz generiert werden. Sie können sich auf den folgenden Code beziehen: 🎜rrreee🎜After Alles in allem muss der Vorgang, der eine Verschlüsselung erfordert, nur einmal im Projektlebenszyklus ausgeführt werden, sodass wir nur eine Toolklasse schreiben und aufrufen müssen. 🎜

Das obige ist der detaillierte Inhalt vonWie Springboot die Verschlüsselung von Klartext-Passwörtern in Konfigurationsdateien implementiert. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen