• Spring Boot 配置文件这样加密,才足够安全!


    1. 前景

    在使用Springboot时,通常很多信息都是在application.yml中直接明文配置的,比如数据库链接信息,redis链接信息等等。但是这样是不安全的。

    所以需要对敏感数据进行加密,这样防止密码泄露

    Jasypt这个库为我们解决了这个问题,实现了springboot配置的自定加密加密

    2. 简单使用

    源码对应地址:

    http://gitlab.sea-clouds.cn/csdn/spring-boot-csdn/-/tree/master/05-spring-boot-jasypt

    2.1 引入依赖

    1. <properties>
    2.     <maven.compiler.source>11</maven.compiler.source>
    3.     <maven.compiler.target>11</maven.compiler.target>
    4. </properties>
    5. <dependencyManagement>
    6.     <dependencies>
    7.         <dependency>
    8.             <groupId>org.springframework.boot</groupId>
    9.             <artifactId>spring-boot-dependencies</artifactId>
    10.             <version>2.4.0</version>
    11.             <type>pom</type>
    12.             <scope>import</scope>
    13.         </dependency>
    14.     </dependencies>
    15. </dependencyManagement>
    16. <dependencies>
    17.     <!-- web 和 测试 -->
    18.     <dependency>
    19.         <groupId>org.springframework.boot</groupId>
    20.         <artifactId>spring-boot-starter-web</artifactId>
    21.     </dependency>
    22.     <dependency>
    23.         <groupId>org.springframework.boot</groupId>
    24.         <artifactId>spring-boot-starter-test</artifactId>
    25.     </dependency>
    26.     <dependency>
    27.         <groupId>junit</groupId>
    28.         <artifactId>junit</artifactId>
    29.         <scope>test</scope>
    30.     </dependency>
    31.     <!-- jdbc -->
    32.     <dependency>
    33.         <groupId>org.springframework.boot</groupId>
    34.         <artifactId>spring-boot-starter-jdbc</artifactId>
    35.     </dependency>
    36.     <dependency>
    37.         <groupId>mysql</groupId>
    38.         <artifactId>mysql-connector-java</artifactId>
    39.     </dependency>
    40.     <!-- jasypt 加密 -->
    41.     <dependency>
    42.         <groupId>com.github.ulisesbocchio</groupId>
    43.         <artifactId>jasypt-spring-boot-starter</artifactId>
    44.         <version>3.0.3</version>
    45.     </dependency>
    46.     <dependency>
    47.         <groupId>org.projectlombok</groupId>
    48.         <artifactId>lombok</artifactId>
    49.     </dependency>
    50. </dependencies>

    2.2 配置application信息

    jasypt配置

    1. jasypt:
    2.   encryptor:
    3.     # 加密算法
    4.     algorithm: PBEWITHHMACSHA512ANDAES_256
    5.     # 加密使用的盐
    6.     password: jaspyt_password

    2.3 加密解密测试

    1. /**
    2.  * @author HLH
    3.  * @description: 加密解密测试
    4.  */
    5. @SpringBootTest
    6. @RunWith(SpringRunner.class)
    7. public class JasyptTest {
    8.     @Autowired
    9.     private StringEncryptor stringEncryptor;
    10.     /**
    11.      * 加密解密测试
    12.      */
    13.     @Test
    14.     public void jasyptTest() {
    15.         // 加密
    16.         System.out.println(stringEncryptor.encrypt("root"));    // JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg
    17.         // 解密
    18.         System.out.println(stringEncryptor.decrypt("JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg"));    // root
    19.     }
    20.     /**
    21.      * 手动测试
    22.      */
    23.     @Test
    24.     public void test() {
    25.         PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
    26.         SimpleStringPBEConfig config = new SimpleStringPBEConfig();
    27.         config.setPassword("jaspyt_password");
    28.         config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
    29.         config.setKeyObtentionIterations("1000");
    30.         config.setPoolSize("1");
    31.         config.setProviderName("SunJCE");
    32.         config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
    33.         config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
    34.         config.setStringOutputType("base64");
    35.         encryptor.setConfig(config);
    36.         System.out.println(encryptor.encrypt("root"));    // JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg
    37.     }
    38. }

    3. 使用Jasypt加密后的字符串代替数据库密码

    3.1 使用加密类进行加密

    密码 root 加密之后 XjYnpGd3JGICnxumpFcfRP8J83m265yC/r1FiwLr9Yo1PNbPXQ2xykLHPpy02CZ1

    1. /**
    2.  * 数据库密码加密
    3.  */
    4. @Test
    5. public void encryptPasswored() {
    6.     // 加密
    7.     System.out.println(stringEncryptor.encrypt("root"));    // XjYnpGd3JGICnxumpFcfRP8J83m265yC/r1FiwLr9Yo1PNbPXQ2xykLHPpy02CZ1
    8.     // 解密
    9.     System.out.println(stringEncryptor.decrypt("XjYnpGd3JGICnxumpFcfRP8J83m265yC/r1FiwLr9Yo1PNbPXQ2xykLHPpy02CZ1"));    // root
    10. }

    3.2 替换数据库配置

    1. spring:
    2.   datasource:
    3.     driver-class-name: com.mysql.cj.jdbc.Driver
    4.     url: jdbc:mysql://192.168.10.31/mp
    5.     username: root
    6.     # 使用ENC()包裹,标识为加密之后的,否则无法解密,会报错
    7.     password: ENC(R2H69h1aEgJ3EDPLXAVQ5CxZJWtl8EvqIJUtlATRt6om4w46/J+blu2JAvkR7Yvp)

    3.3 测试

    1. @Autowired
    2. private DataSource dataSource;
    3. /**
    4.  * 测试加密之后的数据源使用是否正常
    5.  *  查看是否能正常获取链接
    6.  */
    7. @Test
    8. public void datasourceTest() throws SQLException {
    9.     Connection connection = dataSource.getConnection();
    10.     System.out.println(connection);     // HikariProxyConnection@1487059223 wrapping com.mysql.cj.jdbc.ConnectionImpl@48904d5a
    11.     connection.close();
    12. }

    4. Jasypt配置详解

    所有配置都在JasyptEncryptorConfigurationProperties类中定义,我们只需要在yml中配置属性,即可达到重写的目的

    Jasypt使用StringEncryptor来解密属性。如果Spring上下文中找不到自定义的StringEncryptor,就会自动创建一个,可以通过以下属性进行配置

    唯一需要的属性是加密的盐,其余的可以使用默认值。虽然所有这些属性都可以在属性文件中生命,但加密所使用的盐不应该存储在属性文件中,而是应该通过系统属性、命令行参数或者环境变量传递,只要他的名称是jasypt.encryptor.password,它就可以工作。

    倒数第二个属性jasypt.encryptor.proxyPropertySources用于只是jasypt spring boot如何拦截属性值进行解密。默认值false使用PropertySourceEnumerablePropertySourceMapPropertySource的自定义包装器实现。当为true时,拦截机制将在每个特定的PropertySource实现上使用CGLib代理。在某些必须保留原始PropertySource类型的场景中,这可能很有用。

    5. 自定义加密

    默认情况下,bean容器会配置LazyJasyptSringEncryptor

    5.1 官方配置

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

    1. @Bean(
    2.     name = {"lazyJasyptStringEncryptor"}
    3. )
    4. public StringEncryptor stringEncryptor(EnvCopy envCopy, BeanFactory bf) {
    5.     String customEncryptorBeanName = envCopy.get().resolveRequiredPlaceholders(ENCRYPTOR_BEAN_PLACEHOLDER);
    6.     boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.bean");
    7.     return new DefaultLazyEncryptor(envCopy.get(), customEncryptorBeanName, isCustom, bf);
    8. }

    5.2 自定义加密

    可以在Spring上下文中共自定义自己的StringEncryptor Bean,默认的加密程序将被忽略

    注意

    自定义Bean的名称必须为 jasyptStringEncryptor,否则解密不生效

    自定义注入bean

    1. /**
    2.  * 加入 StringEncryptor 加密解密类
    3.  *      beanName 必须为 jasyptStringEncryptor 才能是自定义的生效
    4.  *      configProps 为jasypt框架中读取的配置类,就不用自己读取了
    5.  */
    6. @Bean("jasyptStringEncryptor")
    7. public StringEncryptor jasyptStringEncryptor(Singleton<JasyptEncryptorConfigurationProperties> configProps) {
    8.     PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
    9.     JasyptEncryptorConfigurationProperties jasyptProperties = configProps.get();
    10.     SimpleStringPBEConfig config = new SimpleStringPBEConfig();
    11.     config.setPassword(jasyptProperties.getPassword());
    12.     config.setAlgorithm(jasyptProperties.getAlgorithm());
    13.     config.setKeyObtentionIterations(jasyptProperties.getKeyObtentionIterations());
    14.     config.setPoolSize(jasyptProperties.getPoolSize());
    15.     config.setProviderName(jasyptProperties.getProviderName());
    16.     config.setSaltGeneratorClassName(jasyptProperties.getSaltGeneratorClassname());
    17.     config.setIvGeneratorClassName(jasyptProperties.getIvGeneratorClassname());
    18.     config.setStringOutputType(jasyptProperties.getStringOutputType());
    19.     encryptor.setConfig(config);
    20.     return encryptor;
    21. }

    6. 自定义属性探测器

    属性探测器为判断一个属性值是否为加密后的字符串,并且截取真实字符串

    6.1 官方处理流程

    6.1.2 注入

    EncryptablePropertyResolverConfiguration类中

    1. @Bean(
    2.     name = {"lazyEncryptablePropertyDetector"}
    3. )
    4. public EncryptablePropertyDetector encryptablePropertyDetector(EnvCopy envCopy, BeanFactory bf) {
    5.     String customDetectorBeanName = envCopy.get().resolveRequiredPlaceholders(DETECTOR_BEAN_PLACEHOLDER);
    6.     boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.property.detector-bean");
    7.     return new DefaultLazyPropertyDetector(envCopy.get(), customDetectorBeanName, isCustom, bf);
    8. }

    6.1.2 DefaultLazyPropertyDetector

    默认实现是DefaultLazyPropertyDetector,具体代码是

    1. @Slf4j
    2. public class DefaultLazyPropertyDetector implements EncryptablePropertyDetector {
    3.     // 属性探测器
    4.     private Singleton<EncryptablePropertyDetector> singleton;
    5.     public DefaultLazyPropertyDetector(ConfigurableEnvironment environmentString customDetectorBeanName, boolean isCustom, BeanFactory bf) {
    6.         singleton = new Singleton<>(() ->
    7.                 Optional.of(customDetectorBeanName)
    8.                         .filter(bf::containsBean)
    9.                         .map(name -> (EncryptablePropertyDetector) bf.getBean(name))
    10.                         .map(tap(bean -> log.info("Found Custom Detector Bean {} with name: {}", bean, customDetectorBeanName)))
    11.                         .orElseGet(() -> {
    12.                             if(isCustom) {
    13.                                 throw new IllegalStateException(String.format("Property Detector custom Bean not found with name '%s'", customDetectorBeanName));
    14.                             }
    15.                             log.info("Property Detector custom Bean not found with name '{}'. Initializing Default Property Detector", customDetectorBeanName);
    16.                             return createDefault(environment);
    17.                         }));
    18.     }
    19.     public DefaultLazyPropertyDetector(ConfigurableEnvironment environment) {
    20.         // 创建一个属性探测器
    21.         singleton = new Singleton<>(() -> createDefault(environment));
    22.     }
    23.     private DefaultPropertyDetector createDefault(ConfigurableEnvironment environment) {
    24.         // 读取所有的属性
    25.         JasyptEncryptorConfigurationProperties props = JasyptEncryptorConfigurationProperties.bindConfigProps(environment);
    26.         // 创建一个默认的属性探测器,读取配置文件中的前缀和后缀
    27.         return new DefaultPropertyDetector(props.getProperty().getPrefix(), props.getProperty().getSuffix());
    28.     }
    29.     /**
    30.       * 是否为解密格式字符串
    31.       */
    32.     @Override
    33.     public boolean isEncrypted(String property) {
    34.         return singleton.get().isEncrypted(property);
    35.     }
    36.     /**
    37.       * 获取真是的加密后的字符串
    38.       */
    39.     @Override
    40.     public String unwrapEncryptedValue(String property) {
    41.         return singleton.get().unwrapEncryptedValue(property);
    42.     }
    43. }

    在其中是创建了一个DefaultPropertyDetector对象

    6.1.3 DefaultPropertyDetector

    1. public class DefaultPropertyDetector implements EncryptablePropertyDetector {
    2.     // 默认前缀和后缀
    3.     private String prefix = "ENC(";
    4.     private String suffix = ")";
    5.     public DefaultPropertyDetector() {
    6.     }
    7.     public DefaultPropertyDetector(String prefix, String suffix) {
    8.         Assert.notNull(prefix, "Prefix can't be null");
    9.         Assert.notNull(suffix, "Suffix can't be null");
    10.         this.prefix = prefix;
    11.         this.suffix = suffix;
    12.     }
    13.     @Override
    14.     public boolean isEncrypted(String property) {
    15.         if (property == null) {
    16.             return false;
    17.         }
    18.         final String trimmedValue = property.trim();
    19.         return (trimmedValue.startsWith(prefix) &&
    20.                 trimmedValue.endsWith(suffix));
    21.     }
    22.     // 去掉前缀和后缀
    23.     @Override
    24.     public String unwrapEncryptedValue(String property) {
    25.         return property.substring(
    26.                 prefix.length(),
    27.                 (property.length() - suffix.length()));
    28.     }
    29. }

    6.2 自定义规则探测器

    两种方式自定义

    • 提供一个名为encryptablePropertyDetectorEncryptablePropertyDetector类型的Bean来覆盖默认的实现

    • 如果提供的bean名称不为encryptablePropertyDetector,可以通过修改yml中的属性jasypt.encryptor.property.detector-Bean为自己的bean的名称。

    方式

    • 要么自定义类

    • 要么修改yml中的前缀和后缀

    6.2.1 自定义属性探测器,加入容器

    1. /**
    2.  * 自定义属性探测器
    3.  *  beanName为 encryptablePropertyDetector
    4.  */
    5. @Bean(name = "encryptablePropertyDetector")
    6. public EncryptablePropertyDetector encryptablePropertyDetector() {
    7.     return new MyEncryptablePropertyDetector();
    8. }
    9. /**
    10.  * @author HLH
    11.  * @description: 自定义的属性探测器
    12.  * @email 17703595860@163.com
    13.  * @date : Created in 2021/8/19 20:01
    14.  */
    15. public class MyEncryptablePropertyDetector implements EncryptablePropertyDetector {
    16.     /**
    17.      * 是否为可以解密的字符串
    18.      * @param value 全部的字符串
    19.      * @return 是否是解密的字符串,true,是,false,否
    20.      */
    21.     @Override
    22.     public boolean isEncrypted(String value) {
    23.         if (value != null) {
    24.             return value.startsWith("ENC@");    // 自定义规则为 ENC@开头
    25.         }
    26.         return false;
    27.     }
    28.     /**
    29.      * 截取到除了标识之后的值
    30.      * @param value 带前缀
    31.      * @return string 去掉标识符的字符串
    32.      */
    33.     @Override
    34.     public String unwrapEncryptedValue(String value) {
    35.         return value.substring("ENC@".length());        // 截取ENC@之后的字符串
    36.     }
    37. }

    yml中的配置

    1. jasypt:
    2.   encryptor:
    3.     # 加密算法
    4.     algorithm: PBEWITHHMACSHA512ANDAES_256
    5.     # 加密使用的盐
    6.     password: jaspyt_password
    7.     property:
    8.        # 修改默认的前缀和后缀,如果自定义属性探测器,那么此项配置不起作用
    9.        # prefix: ENC_(
    10.        # suffix: )
    11.        # 自定义的属性探测器,如果这个是自定义的,那么上述的前缀后缀不生效
    12.        detector-bean: encryptablePropertyDetector

    6.2.2 修改yml中的配置

    1. spring:
    2.   datasource:
    3.     driver-class-name: com.mysql.cj.jdbc.Driver
    4.     url: jdbc:mysql://192.168.10.31/mp
    5.     username: root
    6.     # 使用ENC()包裹,标识为加密之后的,否则无法解密,会报错
    7.     # 自定义规则之后,使用ENC@开头
    8.     password: ENC@JSrINYe4IBotHndGjX1hnmY3mtPNUJlXjP12cx1+pHqUz2FNXGPu3Frnajh3QCXg

    7. 自定义规则的前缀和后缀

    在上述说明中,在DefaultLazyPropertyDetector中是默认是通过配置文件中的规则进行匹配的。默认规则是以ENC(开头,以)结尾,可以复写配置来自定义前缀和后缀

    上面第6条是自定义了属性探测器,包括了定义规则和过滤字符串

    如果只是想自定义前缀和后缀,那么可以直接修改yml中的配置来修改自定义的前缀和后缀

    1. jasypt:
    2.   encryptor:
    3.     # 加密算法
    4.     algorithm: PBEWITHHMACSHA512ANDAES_256
    5.     # 加密使用的盐
    6.     password: jaspyt_password
    7.     property:
    8.       # 修改默认的前缀和后缀,如果自定义属性探测器,那么此项配置不起作用
    9.       prefix: ENC(
    10.       suffix: )

    8. 直接自定义解密规则

    上述6和7自定义了解密字符串的规则和解密字符串的过滤,但是真正的解析处理还是Jasypt框架来负责的。我们也可以直接自定义解密的一系列流程。

    8.1 官方处理流程

    8.1.1 官方的注入

    EncryptablePropertyResolverConfiguration类中

    1. @Bean(
    2.     name = {"lazyEncryptablePropertyResolver"}
    3. )
    4. public EncryptablePropertyResolver encryptablePropertyResolver(@Qualifier("lazyEncryptablePropertyDetector") EncryptablePropertyDetector propertyDetector, @Qualifier("lazyJasyptStringEncryptor") StringEncryptor encryptor, BeanFactory bf, EnvCopy envCopy, ConfigurableEnvironment environment) {
    5.     String customResolverBeanName = envCopy.get().resolveRequiredPlaceholders(RESOLVER_BEAN_PLACEHOLDER);
    6.     boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.property.resolver-bean");
    7.     return new DefaultLazyPropertyResolver(propertyDetector, encryptor, customResolverBeanName, isCustom, bf, environment);
    8. }

    默认注入的是DefaultLazyPropertyResolver但是在其中创建的是EncryptablePropertyResolver对象

    8.1.2 EncryptablePropertyResolver

    1. 官方默认是通过EncryptablePropertyResolver接口来处理解析字符串的

    1. public interface EncryptablePropertyResolver {
    2.     /**
    3.      * 处理所有属性的解密处理
    4.      * 如果为检测到加密规则,那么返回实际为相同的字符创
    5.      *
    6.      * @param value 属性值
    7.      * @return 如果值未加密,返回原值,如果加密,返回加密之后的值
    8.      */
    9.     String resolvePropertyValue(String value);
    10. }
    1. 其真实性使用的实现类是DefaultPropertyResolver用来真正处理解析。就是通过调用上文中的StringEncryptor处理解密,使用EncryptablePropertyDetector定义的解密字符串规则定义是否为加密的字符串

    1. public class DefaultPropertyResolver implements EncryptablePropertyResolver {
    2.     private final Environment environment;
    3.     // 默认的或者自定义的StringEncryptor,用来解密
    4.     private StringEncryptor encryptor;
    5.     // 默认的或者自定义的EncryptablePropertyDetector,用来定义是否为加密的字符串
    6.     private EncryptablePropertyDetector detector;
    7.     public DefaultPropertyResolver(StringEncryptor encryptor, Environment environment) {
    8.         this(encryptor, new DefaultPropertyDetector(), environment);
    9.     }
    10.     public DefaultPropertyResolver(StringEncryptor encryptor, EncryptablePropertyDetector detector, Environment environment) {
    11.         this.environment = environment;
    12.         Assert.notNull(encryptor, "String encryptor can't be null");
    13.         Assert.notNull(detector, "Encryptable Property detector can't be null");
    14.         this.encryptor = encryptor;
    15.         this.detector = detector;
    16.     }
    17.     @Override
    18.     public String resolvePropertyValue(String value) {
    19.         return Optional.ofNullable(value)
    20.                 .map(environment::resolvePlaceholders)
    21.                 .filter(detector::isEncrypted)  // 如果经过属性探测器确认的,才继续
    22.                 .map(resolvedValue -> {
    23.                     try {
    24.                         String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim()); // 过滤加密规则后的字符串
    25.                         String resolvedProperty = environment.resolvePlaceholders(unwrappedProperty); 
    26.                         return encryptor.decrypt(resolvedProperty); // 解密
    27.                     } catch (EncryptionOperationNotPossibleException e) {
    28.                         throw new DecryptionException("Unable to decrypt: " + value + ". Decryption of Properties failed,  make sure encryption/decryption " +
    29.                                 "passwords match", e);
    30.                     }
    31.                 })
    32.                 .orElse(value);
    33.     }
    34. }

    8.2 自定义的解密逻辑

    编写自己的解密逻辑类

    加入spring容器,命名为encryptablePropertyResolver,或者通过yml方式配置自定义bean名称

    1. @Bean("encryptablePropertyResolver")
    2. public EncryptablePropertyResolver encryptablePropertyResolver(
    3.         StringEncryptor jasyptStringEncryptor, EncryptablePropertyDetector encryptablePropertyDetector) {
    4.     return new MyEncryptablePropertyResolver(jasyptStringEncryptor, encryptablePropertyDetector);
    5. }
    6. /**
    7.  * @author HLH
    8.  * @description: 直接自定义解密规则
    9.  * @email 17703595860@163.com
    10.  * @date : Created in 2021/8/21 21:22
    11.  */
    12. public class MyEncryptablePropertyResolver implements EncryptablePropertyResolver {
    13.     // 处理解密
    14.     private final StringEncryptor encryptor;
    15.     // 属性探测器
    16.     private final EncryptablePropertyDetector detector;
    17.     public MyEncryptablePropertyResolver(StringEncryptor encryptor, EncryptablePropertyDetector detector) {
    18.         this.encryptor = encryptor;
    19.         this.detector = detector;
    20.     }
    21.     /**
    22.      * 处理真正的解密逻辑
    23.      * @param value 原始值
    24.      * @return 如果值未加密,返回原值,如果加密,返回加密之后的值
    25.      */
    26.     @Override
    27.     public String resolvePropertyValue(String value) {
    28.         return Optional.ofNullable(value)
    29.                 .filter(detector::isEncrypted)  // 如果经过属性探测器确认的,才继续
    30.                 .map(resolvedValue -> {
    31.                     try {
    32.                         String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim()); // 过滤加密规则后的字符串
    33.                         return encryptor.decrypt(unwrappedProperty); // 解密
    34.                     } catch (EncryptionOperationNotPossibleException e) {
    35.                         throw new DecryptionException("Unable to decrypt: " + value + ". Decryption of Properties failed,  make sure encryption/decryption " +
    36.                                 "passwords match", e);
    37.                     }
    38.                 })
    39.                 .orElse(value);
    40.     }
    41. }

    yml配置

    1. jasypt:
    2.   encryptor:
    3.     # 加密算法
    4.     algorithm: PBEWITHHMACSHA512ANDAES_256
    5.     # 加密使用的盐
    6.     password: jaspyt_password
    7.     property:
    8.        # 修改默认的前缀和后缀,如果自定义属性探测器,那么此项配置不起作用
    9.        # prefix: ENC_(
    10.        # suffix: )
    11.        # 自定义的属性探测器,如果这个是自定义的,那么上述的前缀后缀不生效
    12.        detector-bean: encryptablePropertyDetector
    13.        # 自定义解密逻辑类 如果配置了,默认的解析器将不工作
    14.        resolver-bean: encryptablePropertyResolver

    9. 自定义过滤器

    在Jasypt-spring-boot中,引入了过滤器

    过滤器filter允许过滤某些属性,不进行解密。默认情况下,jasypt.encryptor开头的所有属性都会将从检查项中排除掉。这是为了配置Bean,在加载时循环依赖

    9.1 默认处理流程

    9.1.1 官方的注入

    EncryptablePropertyResolverConfiguration类中

    1. @Bean(
    2.     name = {"lazyEncryptablePropertyFilter"}
    3. )
    4. public EncryptablePropertyFilter encryptablePropertyFilter(EnvCopy envCopy, ConfigurableBeanFactory bf) {
    5.     String customFilterBeanName = envCopy.get().resolveRequiredPlaceholders(FILTER_BEAN_PLACEHOLDER);
    6.     boolean isCustom = envCopy.get().containsProperty("jasypt.encryptor.property.filter-bean");
    7.     return new DefaultLazyPropertyFilter(envCopy.get(), customFilterBeanName, isCustom, bf);
    8. }

    于上面的逻辑一样,在DefaultLazyPropertyFilter中其实是新建了一个EncryptablePropertyFilter对象,默认实现类是DefaultPropertyFilter

    9.1.2 DefaultPropertyFilter

    1. public class DefaultPropertyFilter implements EncryptablePropertyFilter {
    2.     // 过滤的和包含的,优先读取配置文件的
    3.     private final List<String> includeSourceNames;
    4.     private final List<String> excludeSourceNames;
    5.     private final List<String> includePropertyNames;
    6.     private final List<String> excludePropertyNames;
    7.     public DefaultPropertyFilter() {
    8.         includeSourceNames = null;
    9.         includePropertyNames = null;
    10.         excludeSourceNames = null;
    11.         excludePropertyNames = null;
    12.     }
    13.     public DefaultPropertyFilter(List<String> includeSourceNames, List<String> excludeSourceNames, List<String> includePropertyNames, List<String> excludePropertyNames) {
    14.         this.includeSourceNames = includeSourceNames;
    15.         this.excludeSourceNames = excludeSourceNames;
    16.         this.includePropertyNames = includePropertyNames;
    17.         this.excludePropertyNames = excludePropertyNames;
    18.     }
    19.     // 是否拦截
    20.     @Override
    21.     public boolean shouldInclude(PropertySource<?> sourceString name) {
    22.         // 如果上述四个都没有配置,那么全部放行
    23.         if (isIncludeAll()) {
    24.             return true;
    25.         }
    26.         // 如果是不包含的,返回false,就过滤掉了
    27.         if (isMatch(source.getName(), excludeSourceNames) || isMatch(name, excludePropertyNames)) {
    28.             return false;
    29.         }
    30.         // 如果是包含的,就放行
    31.         return isIncludeUnset() || isMatch(source.getName(), includeSourceNames) || isMatch(name, includePropertyNames);
    32.     }
    33.     private boolean isIncludeAll() {
    34.         return isIncludeUnset() && isExcludeUnset();
    35.     }
    36.     private boolean isIncludeUnset() {
    37.         return isEmpty(includeSourceNames) && isEmpty(includePropertyNames);
    38.     }
    39.     private boolean isExcludeUnset() {
    40.         return isEmpty(excludeSourceNames) && isEmpty(excludePropertyNames);
    41.     }
    42.     private boolean isEmpty(List<String> patterns) {
    43.         return patterns == null || patterns.isEmpty();
    44.     }
    45.     // 传递的配置其实是正则,进行正则匹配
    46.     private boolean isMatch(String name, List<String> patterns) {
    47.         return name != null && !isEmpty(patterns) && patterns.stream().anyMatch(name::matches);
    48.     }
    49. }

    9.2 自定义过滤器

    方式

    • 要么自定义过滤器

    • 要么修改jasypt.encryptor.property.include-names或者jasypt.encryptor.property.exclude-names配置拦截和放行的资源key

    自定义过滤器类

    加入spring容器,命名为encryptablePropertyFilter

    1. /**
    2.  * 自定义的属性拦截器
    3.  * @param configProps Jasypt官方读取的配置集合
    4.  * @return 自定义属性拦截器
    5.  */
    6. @Bean(name="encryptablePropertyFilter")
    7. public EncryptablePropertyFilter encryptablePropertyFilter(
    8.         Singleton<JasyptEncryptorConfigurationProperties> configProps) {
    9.     return new MyEncryptablePropertyFilter(configProps.get());
    10. }
    11. /**
    12.  * @author HLH
    13.  * @description: 自定义的属性过滤器
    14.  * @email 17703595860@163.com
    15.  * @date : Created in 2021/8/22 13:37
    16.  */
    17. public class MyEncryptablePropertyFilter implements EncryptablePropertyFilter {
    18.     /** jasypt 的所有配置*/
    19.     JasyptEncryptorConfigurationProperties jasyptProperties;
    20.     public MyEncryptablePropertyFilter(JasyptEncryptorConfigurationProperties jasyptProperties) {
    21.         this.jasyptProperties = jasyptProperties;
    22.     }
    23.     @Override
    24.     public boolean shouldInclude(PropertySource<?> sourceString name) {
    25.         List<String> excludeNames = jasyptProperties.getProperty().getFilter().getExcludeNames();
    26.         List<String> includeNames = jasyptProperties.getProperty().getFilter().getIncludeNames();
    27.         if (CollectionUtils.isEmpty(includeNames) && CollectionUtils.isEmpty(excludeNames)) {
    28.             return true;
    29.         }
    30.         if (isMatch(source.getName(), excludeNames) || isMatch(source.getName(), excludeNames)) {
    31.             return false;
    32.         }
    33.         return CollectionUtils.isEmpty(includeNames) ||
    34.                 isMatch(source.getName(), includeNames) ||
    35.                 isMatch(name, includeNames);
    36.     }
    37.     /**
    38.      * 正则判断,如果满足,返回true,如果不满足,返回false
    39.      * @param name 配置的key
    40.      * @param patterns 正则列表
    41.      * @return 如果满足,返回true,如果不满足,返回false
    42.      */
    43.     private boolean isMatch(String name, List<String> patterns) {
    44.         return name != null && !CollectionUtils.isEmpty(patterns) && patterns.stream().anyMatch(name::matches);
    45.     }
    46. }

    yml配置

    1. jasypt:
    2.   encryptor:
    3.     # 加密算法
    4.     algorithm: PBEWITHHMACSHA512ANDAES_256
    5.     # 加密使用的盐
    6.     password: jaspyt_password
    7.     property:
    8.       # 修改默认的前缀和后缀,如果自定义属性探测器,那么此项配置不起作用
    9.       # prefix: ENC_(
    10.       # suffix: )
    11.       # 自定义的属性探测器,如果这个是自定义的,那么上述的前缀后缀不生效
    12.       detector-bean: encryptablePropertyDetector
    13.       # 自定义解密逻辑类 如果配置了,默认的解析器将不工作
    14.       resolver-bean: encryptablePropertyResolver
    15.       # 过滤器的bean
    16.       filter-bean: encryptablePropertyFilter
    17.       # 过滤器配置,正则
    18.       filter:
    19.         # 默认包含的
    20.         include-names:
    21.         # 默认拦截的,默认拦截jasypt.encryptor的配置
    22.         exclude-names:
    23.           - ^jasypt\.encryptor\.*

    10. 使用mvn插件加密解密

    使用代码的方式比较不方便,还需要编码实现,如果不想编码,简单的进行加密解密,就可以使用maven的插件,使用mvn命令进行加密解密

    10.1 引入Jasypt的maven插件

    1. <build>
    2.     <plugins>
    3.         <!-- Jasypt 的maven插件 -->
    4.         <plugin>
    5.             <groupId>com.github.ulisesbocchio</groupId>
    6.             <artifactId>jasypt-maven-plugin</artifactId>
    7.             <version>3.0.2</version>
    8.         </plugin>
    9.     </plugins>
    10. </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自带

    进入项目所在的目录,输入命令,成功加密

    11. 思维导图

    最后再来一张思维导图        

     

  • 相关阅读:
    【嵌入式模块】再探ESP8266,保姆级教程
    Spring的事务机制
    番外篇 | 基于改进YOLOv5的安全帽佩戴检测 | 重参数化结构RepVGG + 空间对象注意力机制RCS-OSA模块
    Electron:窗口、窗口标题和边框
    dockerfile来构建自己的docker镜像
    【用unity实现100个游戏之15】开发一个类保卫萝卜的Unity2D塔防游戏3(附项目源码)
    CSS中如何实现文字描边效果(Text Stroke)?
    基于 Linux 的 web 服务器
    shiro缓存与session持久化
    tomcat映射本地文件路径
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/128181179