⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/HLH_2021/article/details/119854365 「HLH_2021」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

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 引入依赖

<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使用PropertySourceEnumerablePropertySourceMapPropertySource的自定义包装器实现。当为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 自定义规则探测器

两种方式自定义

  • 提供一个名为encryptablePropertyDetectorEncryptablePropertyDetector类型的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 17703595860@163.com
* @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 17703595860@163.com
* @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 17703595860@163.com
* @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自带

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

11. 思维导图

最后再来一张思维导图

文章目录
  1. 1. 1. 前景
  2. 2. 2. 简单使用
    1. 2.0.1. 2.1 引入依赖
    2. 2.0.2. 2.2 配置application信息
    3. 2.0.3. 2.3 加密解密测试
  • 3. 3. 使用Jasypt加密后的字符串代替数据库密码
    1. 3.0.1. 3.1 使用加密类进行加密
    2. 3.0.2. 3.2 替换数据库配置
    3. 3.0.3. 3.3 测试
  • 4. 4. Jasypt配置详解
  • 5. 5. 自定义加密
    1. 5.0.1. 5.1 官方配置
    2. 5.0.2. 5.2 自定义加密
  • 6. 6. 自定义属性探测器
    1. 6.0.1. 6.1 官方处理流程
    2. 6.0.2. 6.2 自定义规则探测器
  • 7. 7. 自定义规则的前缀和后缀
  • 8. 8. 直接自定义解密规则
    1. 8.0.1. 8.1 官方处理流程
    2. 8.0.2. 8.2 自定义的解密逻辑
  • 9. 9. 自定义过滤器
    1. 9.0.1. 9.1 默认处理流程
    2. 9.0.2. 9.2 自定义过滤器
  • 10. 10. 使用mvn插件加密解密
    1. 10.0.1. 10.1 引入Jasypt的maven插件
    2. 10.0.2. 10.2 加密
    3. 10.0.3. 10.3 解密
  • 11. 11. 思维导图