天天看點

SpringBoot 配置檔案這樣加密,才足夠安全

作者:老誠不bug
SpringBoot 配置檔案這樣加密,才足夠安全

1. 前景

在使用Springboot時,通常很多資訊都是在application.yml中直接明文配置的,比如資料庫連結資訊,redis連結資訊等等。但是這樣是不安全的。

是以需要對敏感資料進行加密,這樣防止密碼洩露

Jasypt這個庫為我們解決了這個問題,實作了springboot配置的自定加密加密

2. 簡單使用

源碼對應位址:

gitlab.sea-clouds.cn/csdn/spring…

2.1 引入依賴

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.4.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- web 和 測試 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- jdbc -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- jasypt 加密 -->
    <dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
複制代碼           

2.2 配置application資訊

jasypt配置

jasypt:
  encryptor:
    # 加密算法
    algorithm: PBEWITHHMACSHA512ANDAES_256
    # 加密使用的鹽
    password: jaspyt_password
複制代碼           

2.3 加密解密測試

/**
 * @author HLH
 * @description: 加密解密測試
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class JasyptTest {

    @Autowired
    private StringEncryptor stringEncryptor;

    /**
     * 加密解密測試
     */
    @Test
    public void jasyptTest() {
        // 加密
        System.out.println(stringEncryptor.encrypt("root"));    // JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg
        // 解密
        System.out.println(stringEncryptor.decrypt("JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg"));    // root
    }

    /**
     * 手動測試
     */
    @Test
    public void test() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword("jaspyt_password");
        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);
        System.out.println(encryptor.encrypt("root"));    // JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg
    }

}
複制代碼           

3. 使用Jasypt加密後的字元串代替資料庫密碼

3.1 使用加密類進行加密

密碼 root 加密之後 XjYnpGd3JGICnxumpFcfRP8J83m265yC/r1FiwLr9Yo1PNbPXQ2xykLHPpy02CZ1

/**
 * 資料庫密碼加密
 */
@Test
public void encryptPasswored() {
    // 加密
    System.out.println(stringEncryptor.encrypt("root"));    // XjYnpGd3JGICnxumpFcfRP8J83m265yC/r1FiwLr9Yo1PNbPXQ2xykLHPpy02CZ1
    // 解密
    System.out.println(stringEncryptor.decrypt("XjYnpGd3JGICnxumpFcfRP8J83m265yC/r1FiwLr9Yo1PNbPXQ2xykLHPpy02CZ1"));    // root
}
複制代碼           

3.2 替換資料庫配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.10.31/mp
    username: root
    # 使用ENC()包裹,辨別為加密之後的,否則無法解密,會報錯
    password: ENC(R2H69h1aEgJ3EDPLXAVQ5CxZJWtl8EvqIJUtlATRt6om4w46/J+blu2JAvkR7Yvp)
複制代碼           

3.3 測試

@Autowired
private DataSource dataSource;
/**
 * 測試加密之後的資料源使用是否正常
 *  檢視是否能正常擷取連結
 */
@Test
public void datasourceTest() throws SQLException {
    Connection connection = dataSource.getConnection();
    System.out.println(connection);     // HikariProxyConnection@1487059223 wrapping com.mysql.cj.jdbc.ConnectionImpl@48904d5a
    connection.close();
}
複制代碼           

4. Jasypt配置詳解

所有配置都在JasyptEncryptorConfigurationProperties類中定義,我們隻需要在yml中配置屬性,即可達到重寫的目的

Jasypt使用StringEncryptor來解密屬性。如果Spring上下文中找不到自定義的StringEncryptor,就會自動建立一個,可以通過以下屬性進行配置

唯一需要的屬性是加密的鹽,其餘的可以使用預設值。雖然所有這些屬性都可以在屬性檔案中生命,但加密所使用的鹽不應該存儲在屬性檔案中,而是應該通過系統屬性、指令行參數或者環境變量傳遞,隻要他的名稱是jasypt.encryptor.password,它就可以工作。

倒數第二個屬性jasypt.encryptor.proxyPropertySources用于隻是jasypt spring boot如何攔截屬性值進行解密。預設值false使用PropertySource、EnumerablePropertySource和MapPropertySource的自定義包裝器實作。當為true時,攔截機制将在每個特定的PropertySource實作上使用CGLib代理。在某些必須保留原始PropertySource類型的場景中,這可能很有用。

5. 自定義加密

預設情況下,bean容器會配置LazyJasyptSringEncryptor

5.1 官方配置

官方配置的Bean都是在EncryptablePropertyResolverConfiguration中進行注入的

@Bean(
    name = {"lazyJasyptStringEncryptor"}
)
public StringEncryptor stringEncryptor(EnvCopy envCopy, BeanFactory bf) {
    String customEncryptorBeanName = envCopy.get().resolveRequiredPlaceholders(ENCRYPTOR_BEAN_PLACEHOLDER);
    boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.bean");
    return new DefaultLazyEncryptor(envCopy.get(), customEncryptorBeanName, isCustom, bf);
}
複制代碼           

5.2 自定義加密

可以在Spring上下文中共自定義自己的StringEncryptor Bean,預設的加密程式将被忽略

注意

自定義Bean的名稱必須為 jasyptStringEncryptor,否則解密不生效

自定義注入bean

/**
 * 加入 StringEncryptor 加密解密類
 *      beanName 必須為 jasyptStringEncryptor 才能是自定義的生效
 *      configProps 為jasypt架構中讀取的配置類,就不用自己讀取了
 */
@Bean("jasyptStringEncryptor")
public StringEncryptor jasyptStringEncryptor(Singleton<JasyptEncryptorConfigurationProperties> configProps) {
    PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
    JasyptEncryptorConfigurationProperties jasyptProperties = configProps.get();
    SimpleStringPBEConfig config = new SimpleStringPBEConfig();
    config.setPassword(jasyptProperties.getPassword());
    config.setAlgorithm(jasyptProperties.getAlgorithm());
    config.setKeyObtentionIterations(jasyptProperties.getKeyObtentionIterations());
    config.setPoolSize(jasyptProperties.getPoolSize());
    config.setProviderName(jasyptProperties.getProviderName());
    config.setSaltGeneratorClassName(jasyptProperties.getSaltGeneratorClassname());
    config.setIvGeneratorClassName(jasyptProperties.getIvGeneratorClassname());
    config.setStringOutputType(jasyptProperties.getStringOutputType());
    encryptor.setConfig(config);
    return encryptor;
}
複制代碼           

6. 自定義屬性探測器

屬性探測器為判斷一個屬性值是否為加密後的字元串,并且截取真實字元串

6.1 官方處理流程

6.1.2 注入

在EncryptablePropertyResolverConfiguration類中

@Bean(
    name = {"lazyEncryptablePropertyDetector"}
)
public EncryptablePropertyDetector encryptablePropertyDetector(EnvCopy envCopy, BeanFactory bf) {
    String customDetectorBeanName = envCopy.get().resolveRequiredPlaceholders(DETECTOR_BEAN_PLACEHOLDER);
    boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.property.detector-bean");
    return new DefaultLazyPropertyDetector(envCopy.get(), customDetectorBeanName, isCustom, bf);
}
複制代碼           

6.1.2 DefaultLazyPropertyDetector

預設實作是DefaultLazyPropertyDetector,具體代碼是

@Slf4j
public class DefaultLazyPropertyDetector implements EncryptablePropertyDetector {

    // 屬性探測器
    private Singleton<EncryptablePropertyDetector> singleton;

    public DefaultLazyPropertyDetector(ConfigurableEnvironment environment, String customDetectorBeanName, boolean isCustom, BeanFactory bf) {
        singleton = new Singleton<>(() ->
                Optional.of(customDetectorBeanName)
                        .filter(bf::containsBean)
                        .map(name -> (EncryptablePropertyDetector) bf.getBean(name))
                        .map(tap(bean -> log.info("Found Custom Detector Bean {} with name: {}", bean, customDetectorBeanName)))
                        .orElseGet(() -> {
                            if(isCustom) {
                                throw new IllegalStateException(String.format("Property Detector custom Bean not found with name '%s'", customDetectorBeanName));
                            }
                            log.info("Property Detector custom Bean not found with name '{}'. Initializing Default Property Detector", customDetectorBeanName);
                            return createDefault(environment);
                        }));
    }

    public DefaultLazyPropertyDetector(ConfigurableEnvironment environment) {
        // 建立一個屬性探測器
        singleton = new Singleton<>(() -> createDefault(environment));
    }

    private DefaultPropertyDetector createDefault(ConfigurableEnvironment environment) {
        // 讀取所有的屬性
        JasyptEncryptorConfigurationProperties props = JasyptEncryptorConfigurationProperties.bindConfigProps(environment);
        // 建立一個預設的屬性探測器,讀取配置檔案中的字首和字尾
        return new DefaultPropertyDetector(props.getProperty().getPrefix(), props.getProperty().getSuffix());
    }

    /**
      * 是否為解密格式字元串
      */
    @Override
    public boolean isEncrypted(String property) {
        return singleton.get().isEncrypted(property);
    }

    /**
      * 擷取真是的加密後的字元串
      */
    @Override
    public String unwrapEncryptedValue(String property) {
        return singleton.get().unwrapEncryptedValue(property);
    }
}
複制代碼           

在其中是建立了一個DefaultPropertyDetector對象

6.1.3 DefaultPropertyDetector

public class DefaultPropertyDetector implements EncryptablePropertyDetector {

    // 預設字首和字尾
    private String prefix = "ENC(";
    private String suffix = ")";

    public DefaultPropertyDetector() {
    }

    public DefaultPropertyDetector(String prefix, String suffix) {
        Assert.notNull(prefix, "Prefix can't be null");
        Assert.notNull(suffix, "Suffix can't be null");
        this.prefix = prefix;
        this.suffix = suffix;
    }

    @Override
    public boolean isEncrypted(String property) {
        if (property == null) {
            return false;
        }
        final String trimmedValue = property.trim();
        return (trimmedValue.startsWith(prefix) &&
                trimmedValue.endsWith(suffix));
    }

    // 去掉字首和字尾
    @Override
    public String unwrapEncryptedValue(String property) {
        return property.substring(
                prefix.length(),
                (property.length() - suffix.length()));
    }
}
複制代碼           

6.2 自定義規則探測器

兩種方式自定義

  • 提供一個名為encryptablePropertyDetector的EncryptablePropertyDetector類型的Bean來覆寫預設的實作
  • 如果提供的bean名稱不為encryptablePropertyDetector,可以通過修改yml中的屬性jasypt.encryptor.property.detector-Bean為自己的bean的名稱。

方式

  • 要麼自定義類
  • 要麼修改yml中的字首和字尾

6.2.1 自定義屬性探測器,加入容器

/**
 * 自定義屬性探測器
 *  beanName為 encryptablePropertyDetector
 */
@Bean(name = "encryptablePropertyDetector")
public EncryptablePropertyDetector encryptablePropertyDetector() {
    return new MyEncryptablePropertyDetector();
}


/**
 * @author HLH
 * @description: 自定義的屬性探測器
 * @email [email protected]
 * @date : Created in 2021/8/19 20:01
 */
public class MyEncryptablePropertyDetector implements EncryptablePropertyDetector {

    /**
     * 是否為可以解密的字元串
     * @param value 全部的字元串
     * @return 是否是解密的字元串,true,是,false,否
     */
    @Override
    public boolean isEncrypted(String value) {
        if (value != null) {
            return value.startsWith("ENC@");    // 自定義規則為 ENC@開頭
        }
        return false;
    }

    /**
     * 截取到除了辨別之後的值
     * @param value 帶字首
     * @return string 去掉辨別符的字元串
     */
    @Override
    public String unwrapEncryptedValue(String value) {
        return value.substring("ENC@".length());        // 截取ENC@之後的字元串
    }
}
複制代碼           

yml中的配置

jasypt:
  encryptor:
    # 加密算法
    algorithm: PBEWITHHMACSHA512ANDAES_256
    # 加密使用的鹽
    password: jaspyt_password
    property:
       # 修改預設的字首和字尾,如果自定義屬性探測器,那麼此項配置不起作用
       # prefix: ENC_(
       # suffix: )
       # 自定義的屬性探測器,如果這個是自定義的,那麼上述的字首字尾不生效
       detector-bean: encryptablePropertyDetector
複制代碼           

6.2.2 修改yml中的配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.10.31/mp
    username: root
    # 使用ENC()包裹,辨別為加密之後的,否則無法解密,會報錯
    # 自定義規則之後,使用ENC@開頭
    password: ENC@JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg
複制代碼           

7. 自定義規則的字首和字尾

在上述說明中,在DefaultLazyPropertyDetector中是預設是通過配置檔案中的規則進行比對的。預設規則是以ENC(開頭,以)結尾,可以複寫配置來自定義字首和字尾

上面第6條是自定義了屬性探測器,包括了定義規則和過濾字元串

如果隻是想自定義字首和字尾,那麼可以直接修改yml中的配置來修改自定義的字首和字尾

jasypt:
  encryptor:
    # 加密算法
    algorithm: PBEWITHHMACSHA512ANDAES_256
    # 加密使用的鹽
    password: jaspyt_password
    property:
      # 修改預設的字首和字尾,如果自定義屬性探測器,那麼此項配置不起作用
      prefix: ENC(
      suffix: )
複制代碼           

8. 直接自定義解密規則

上述6和7自定義了解密字元串的規則和解密字元串的過濾,但是真正的解析處理還是Jasypt架構來負責的。我們也可以直接自定義解密的一系列流程。

8.1 官方處理流程

8.1.1 官方的注入

在EncryptablePropertyResolverConfiguration類中

@Bean(
    name = {"lazyEncryptablePropertyResolver"}
)
public EncryptablePropertyResolver encryptablePropertyResolver(@Qualifier("lazyEncryptablePropertyDetector") EncryptablePropertyDetector propertyDetector, @Qualifier("lazyJasyptStringEncryptor") StringEncryptor encryptor, BeanFactory bf, EnvCopy envCopy, ConfigurableEnvironment environment) {
    String customResolverBeanName = envCopy.get().resolveRequiredPlaceholders(RESOLVER_BEAN_PLACEHOLDER);
    boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.property.resolver-bean");
    return new DefaultLazyPropertyResolver(propertyDetector, encryptor, customResolverBeanName, isCustom, bf, environment);
}
複制代碼           

預設注入的是DefaultLazyPropertyResolver但是在其中建立的是EncryptablePropertyResolver對象

8.1.2 EncryptablePropertyResolver

  1. 官方預設是通過EncryptablePropertyResolver接口來處了解析字元串的
public interface EncryptablePropertyResolver {

    /**
     * 處理所有屬性的解密處理
     * 如果為檢測到加密規則,那麼傳回實際為相同的字元創
     *
     * @param value 屬性值
     * @return 如果值未加密,傳回原值,如果加密,傳回加密之後的值
     */
    String resolvePropertyValue(String value);
}
複制代碼           
  1. 其真實性使用的實作類是DefaultPropertyResolver用來真正處了解析。就是通過調用上文中的StringEncryptor處了解密,使用EncryptablePropertyDetector定義的解密字元串規則定義是否為加密的字元串
public class DefaultPropertyResolver implements EncryptablePropertyResolver {

    private final Environment environment;
    // 預設的或者自定義的StringEncryptor,用來解密
    private StringEncryptor encryptor;
    // 預設的或者自定義的EncryptablePropertyDetector,用來定義是否為加密的字元串
    private EncryptablePropertyDetector detector;

    public DefaultPropertyResolver(StringEncryptor encryptor, Environment environment) {
        this(encryptor, new DefaultPropertyDetector(), environment);
    }

    public DefaultPropertyResolver(StringEncryptor encryptor, EncryptablePropertyDetector detector, Environment environment) {
        this.environment = environment;
        Assert.notNull(encryptor, "String encryptor can't be null");
        Assert.notNull(detector, "Encryptable Property detector can't be null");
        this.encryptor = encryptor;
        this.detector = detector;
    }

    @Override
    public String resolvePropertyValue(String value) {
        return Optional.ofNullable(value)
                .map(environment::resolvePlaceholders)
                .filter(detector::isEncrypted)  // 如果經過屬性探測器确認的,才繼續
                .map(resolvedValue -> {
                    try {
                        String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim()); // 過濾加密規則後的字元串
                        String resolvedProperty = environment.resolvePlaceholders(unwrappedProperty); 
                        return encryptor.decrypt(resolvedProperty); // 解密
                    } catch (EncryptionOperationNotPossibleException e) {
                        throw new DecryptionException("Unable to decrypt: " + value + ". Decryption of Properties failed,  make sure encryption/decryption " +
                                "passwords match", e);
                    }
                })
                .orElse(value);
    }
}
複制代碼           

8.2 自定義的解密邏輯

編寫自己的解密邏輯類

加入spring容器,命名為encryptablePropertyResolver,或者通過yml方式配置自定義bean名稱

@Bean("encryptablePropertyResolver")
public EncryptablePropertyResolver encryptablePropertyResolver(
        StringEncryptor jasyptStringEncryptor, EncryptablePropertyDetector encryptablePropertyDetector) {
    return new MyEncryptablePropertyResolver(jasyptStringEncryptor, encryptablePropertyDetector);
}

/**
 * @author HLH
 * @description: 直接自定義解密規則
 * @email [email protected]
 * @date : Created in 2021/8/21 21:22
 */
public class MyEncryptablePropertyResolver implements EncryptablePropertyResolver {

    // 處了解密
    private final StringEncryptor encryptor;
    // 屬性探測器
    private final EncryptablePropertyDetector detector;

    public MyEncryptablePropertyResolver(StringEncryptor encryptor, EncryptablePropertyDetector detector) {
        this.encryptor = encryptor;
        this.detector = detector;
    }

    /**
     * 處理真正的解密邏輯
     * @param value 原始值
     * @return 如果值未加密,傳回原值,如果加密,傳回加密之後的值
     */
    @Override
    public String resolvePropertyValue(String value) {
        return Optional.ofNullable(value)
                .filter(detector::isEncrypted)  // 如果經過屬性探測器确認的,才繼續
                .map(resolvedValue -> {
                    try {
                        String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim()); // 過濾加密規則後的字元串
                        return encryptor.decrypt(unwrappedProperty); // 解密
                    } catch (EncryptionOperationNotPossibleException e) {
                        throw new DecryptionException("Unable to decrypt: " + value + ". Decryption of Properties failed,  make sure encryption/decryption " +
                                "passwords match", e);
                    }
                })
                .orElse(value);
    }
}
複制代碼           

yml配置

jasypt:
  encryptor:
    # 加密算法
    algorithm: PBEWITHHMACSHA512ANDAES_256
    # 加密使用的鹽
    password: jaspyt_password
    property:
       # 修改預設的字首和字尾,如果自定義屬性探測器,那麼此項配置不起作用
       # prefix: ENC_(
       # suffix: )
       # 自定義的屬性探測器,如果這個是自定義的,那麼上述的字首字尾不生效
       detector-bean: encryptablePropertyDetector
       # 自定義解密邏輯類 如果配置了,預設的解析器将不工作
       resolver-bean: encryptablePropertyResolver
複制代碼           

9. 自定義過濾器

在Jasypt-spring-boot中,引入了過濾器

過濾器filter允許過濾某些屬性,不進行解密。預設情況下,jasypt.encryptor開頭的所有屬性都會将從檢查項中排除掉。這是為了配置Bean,在加載時循環依賴

9.1 預設處理流程

9.1.1 官方的注入

在EncryptablePropertyResolverConfiguration類中

@Bean(
    name = {"lazyEncryptablePropertyFilter"}
)
public EncryptablePropertyFilter encryptablePropertyFilter(EnvCopy envCopy, ConfigurableBeanFactory bf) {
    String customFilterBeanName = envCopy.get().resolveRequiredPlaceholders(FILTER_BEAN_PLACEHOLDER);
    boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.property.filter-bean");
    return new DefaultLazyPropertyFilter(envCopy.get(), customFilterBeanName, isCustom, bf);
}
複制代碼           

于上面的邏輯一樣,在DefaultLazyPropertyFilter中其實是建立了一個EncryptablePropertyFilter對象,預設實作類是DefaultPropertyFilter

9.1.2 DefaultPropertyFilter

public class DefaultPropertyFilter implements EncryptablePropertyFilter {

    // 過濾的和包含的,優先讀取配置檔案的
    private final List<String> includeSourceNames;
    private final List<String> excludeSourceNames;
    private final List<String> includePropertyNames;
    private final List<String> excludePropertyNames;

    public DefaultPropertyFilter() {
        includeSourceNames = null;
        includePropertyNames = null;
        excludeSourceNames = null;
        excludePropertyNames = null;
    }

    public DefaultPropertyFilter(List<String> includeSourceNames, List<String> excludeSourceNames, List<String> includePropertyNames, List<String> excludePropertyNames) {
        this.includeSourceNames = includeSourceNames;
        this.excludeSourceNames = excludeSourceNames;
        this.includePropertyNames = includePropertyNames;
        this.excludePropertyNames = excludePropertyNames;
    }

    // 是否攔截
    @Override
    public boolean shouldInclude(PropertySource<?> source, String name) {
        // 如果上述四個都沒有配置,那麼全部放行
        if (isIncludeAll()) {
            return true;
        }

        // 如果是不包含的,傳回false,就過濾掉了
        if (isMatch(source.getName(), excludeSourceNames) || isMatch(name, excludePropertyNames)) {
            return false;
        }

        // 如果是包含的,就放行
        return isIncludeUnset() || isMatch(source.getName(), includeSourceNames) || isMatch(name, includePropertyNames);

    }

    private boolean isIncludeAll() {
        return isIncludeUnset() && isExcludeUnset();
    }

    private boolean isIncludeUnset() {
        return isEmpty(includeSourceNames) && isEmpty(includePropertyNames);
    }

    private boolean isExcludeUnset() {
        return isEmpty(excludeSourceNames) && isEmpty(excludePropertyNames);
    }

    private boolean isEmpty(List<String> patterns) {
        return patterns == null || patterns.isEmpty();
    }

    // 傳遞的配置其實是正則,進行正則比對
    private boolean isMatch(String name, List<String> patterns) {
        return name != null && !isEmpty(patterns) && patterns.stream().anyMatch(name::matches);
    }
}
複制代碼           

9.2 自定義過濾器

方式

  • 要麼自定義過濾器
  • 要麼修改jasypt.encryptor.property.include-names或者jasypt.encryptor.property.exclude-names配置攔截和放行的資源key

自定義過濾器類

加入spring容器,命名為encryptablePropertyFilter

/**
 * 自定義的屬性攔截器
 * @param configProps Jasypt官方讀取的配置集合
 * @return 自定義屬性攔截器
 */
@Bean(name="encryptablePropertyFilter")
public EncryptablePropertyFilter encryptablePropertyFilter(
        Singleton<JasyptEncryptorConfigurationProperties> configProps) {
    return new MyEncryptablePropertyFilter(configProps.get());
}

/**
 * @author HLH
 * @description: 自定義的屬性過濾器
 * @email [email protected]
 * @date : Created in 2021/8/22 13:37
 */
public class MyEncryptablePropertyFilter implements EncryptablePropertyFilter {

    /** jasypt 的所有配置*/
    JasyptEncryptorConfigurationProperties jasyptProperties;

    public MyEncryptablePropertyFilter(JasyptEncryptorConfigurationProperties jasyptProperties) {
        this.jasyptProperties = jasyptProperties;
    }

    @Override
    public boolean shouldInclude(PropertySource<?> source, String name) {
        List<String> excludeNames = jasyptProperties.getProperty().getFilter().getExcludeNames();
        List<String> includeNames = jasyptProperties.getProperty().getFilter().getIncludeNames();
        if (CollectionUtils.isEmpty(includeNames) && CollectionUtils.isEmpty(excludeNames)) {
            return true;
        }

        if (isMatch(source.getName(), excludeNames) || isMatch(source.getName(), excludeNames)) {
            return false;
        }

        return CollectionUtils.isEmpty(includeNames) ||
                isMatch(source.getName(), includeNames) ||
                isMatch(name, includeNames);

    }

    /**
     * 正則判斷,如果滿足,傳回true,如果不滿足,傳回false
     * @param name 配置的key
     * @param patterns 正則清單
     * @return 如果滿足,傳回true,如果不滿足,傳回false
     */
    private boolean isMatch(String name, List<String> patterns) {
        return name != null && !CollectionUtils.isEmpty(patterns) && patterns.stream().anyMatch(name::matches);
    }

}
複制代碼           

yml配置

jasypt:
  encryptor:
    # 加密算法
    algorithm: PBEWITHHMACSHA512ANDAES_256
    # 加密使用的鹽
    password: jaspyt_password
    property:
      # 修改預設的字首和字尾,如果自定義屬性探測器,那麼此項配置不起作用
      # prefix: ENC_(
      # suffix: )
      # 自定義的屬性探測器,如果這個是自定義的,那麼上述的字首字尾不生效
      detector-bean: encryptablePropertyDetector
      # 自定義解密邏輯類 如果配置了,預設的解析器将不工作
      resolver-bean: encryptablePropertyResolver
      # 過濾器的bean
      filter-bean: encryptablePropertyFilter
      # 過濾器配置,正則
      filter:
        # 預設包含的
        include-names:
        # 預設攔截的,預設攔截jasypt.encryptor的配置
        exclude-names:
          - ^jasypt.encryptor.*
複制代碼           

10. 使用mvn插件加密解密

使用代碼的方式比較不友善,還需要編碼實作,如果不想編碼,簡單的進行加密解密,就可以使用maven的插件,使用mvn指令進行加密解密

10.1 引入Jasypt的maven插件

<build>
    <plugins>
        <!-- Jasypt 的maven插件 -->
        <plugin>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-maven-plugin</artifactId>
            <version>3.0.2</version>
        </plugin>
    </plugins>
</build>
複制代碼           

10.2 加密

使用jasypt-maven-plugin插件加密明文密碼:(如果配置項是預設值,可以不指定)

mvn jasypt:encrypt-value -Djasypt.encryptor.password="jaspyt_password" -Djasypt.plugin.value="root" -Djasypt.encryptor.algorithm="PBEWITHHMACSHA512ANDAES_256"
複制代碼           
  • jasypt.encryptor.password 是秘鑰,盡量複雜!不能放在代碼和配置檔案裡面!不能洩漏
  • jasypt.plugin.value 是要加密的明文密碼
  • jasypt.encryptor.algorithm預設加密算法是PBEWITHHMACSHA512ANDAES_256,需要有JCE(Java Cryptography Extension)支援,如果不想安裝JCE,可以使用PBEWithMD5AndDES算法。windows下的jdk自帶

進入項目所在的目錄,輸入指令,成功加密

10.3 解密

使用jasypt-maven-plugin插件解密密文密碼:(如果配置項是預設值,可以不指定)

mvn jasypt:decrypt-value -Djasypt.encryptor.password="jaspyt_password" -Djasypt.plugin.value="pqsp6kvVfBcKoEltxP9MilGGRo8EE506mDWAuTFIKePDXMeArta13bT6Hl8QqVlC" -Djasypt.encryptor.algorithm="PBEWITHHMACSHA512ANDAES_256"
複制代碼           
  • jasypt.encryptor.password 是秘鑰,盡量複雜!不能放在代碼和配置檔案裡面!不能洩漏
  • jasypt.plugin.value 是要加密的明文密碼,有ENC()包裹或者不包裹都可以
  • jasypt.encryptor.algorithm預設加密算法是PBEWITHHMACSHA512ANDAES_256,需要有JCE(Java Cryptography Extension)支援,如果不想安裝JCE,可以使用PBEWithMD5AndDES算法。windows下的jdk自帶

進入項目所在的目錄,輸入指令,成功加密

SpringBoot 配置檔案這樣加密,才足夠安全

11. 思維導圖

最後再來一張思維導圖

SpringBoot 配置檔案這樣加密,才足夠安全
來源:blog.csdn.net/HLH_2021/article/details/119854365

繼續閱讀