通过源码解读 jasypt, 查看其工作机制
jasypt-spring-boot-starter-x.x.x.jar
jasypt 对于 springboot 的支持包, 查看包内类文件:
Dependency management is a critical aspects of any complex project. And doing this manually is less than ideal; the more time you spent on it the less time you have on the other important aspects of the project.
1
2
3
4
5
|-- com.ulisesbocchio.jasyptspringboot
- JasyptSpringBootAutoConfiguration
- JasyptSpringCloudBootstrapConfiguration
|-- META-INF
- spring.factories
1. 查看 spring.factories 文件, 分别配置了 springboot 的自动配置与 springCloud 的自动配置(springcloud 与 springboot 上下文分别初始化, 为父子关系)
1
2
3
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ulisesbocchio.jasyptspringboot.JasyptSpringBootAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.ulisesbocchio.jasyptspringboot.JasyptSpringCloudBootstrapConfiguration
2. 两个类中引入了相同的配置类: @Import({EnableEncryptablePropertiesConfiguration.class})
这个类为 com.ulisesbocchio.jasyptspringboot 包中的类, 是 jasypt 对于 springboot 封装的一些处理类
jasypt 还有核心的包: org.jasypt, 这个包定义了顶级接口以及常用的密码术逻辑
3. 查看 EnableEncryptablePropertiesConfiguration 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
@Import({EncryptablePropertyResolverConfiguration.class, CachingConfiguration.class})
@Slf4j
public class EnableEncryptablePropertiesConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Bean
public static EnableEncryptablePropertiesBeanFactoryPostProcessor enableEncryptablePropertySourcesPostProcessor(final ConfigurableEnvironment environment) {
// 获取配置文件中的配置信息, 是否要代理 propertySource 对象, jasypt 对 ps 对象有两种处理方式, 下面会说. 如果没有, 那么默认为 false
final boolean proxyPropertySources = environment.getProperty("jasypt.encryptor.proxyPropertySources", Boolean.TYPE, false);
// 根据上一步获取的配置, 选择代理模式
final InterceptionMode interceptionMode = proxyPropertySources ? InterceptionMode.PROXY : InterceptionMode.WRAPPER;
// 创建 beanFactoryPostProcessor
return new EnableEncryptablePropertiesBeanFactoryPostProcessor(environment, interceptionMode);
}
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
log.info("Bootstraping jasypt-string-boot auto configuration in context: {}", applicationContext.getId());
}
}
4. 查看上一步 import 的 EncryptablePropertyResolverConfiguration类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
@Configuration
public class EncryptablePropertyResolverConfiguration {
private static final String ENCRYPTOR_BEAN_PLACEHOLDER = "${jasypt.encryptor.bean:jasyptStringEncryptor}";
private static final String DETECTOR_BEAN_PLACEHOLDER = "${jasypt.encryptor.property.detector-bean:encryptablePropertyDetector}";
private static final String RESOLVER_BEAN_PLACEHOLDER = "${jasypt.encryptor.property.resolver-bean:encryptablePropertyResolver}";
private static final String FILTER_BEAN_PLACEHOLDER = "${jasypt.encryptor.property.filter-bean:encryptablePropertyFilter}";
private static final String ENCRYPTOR_BEAN_NAME = "lazyJasyptStringEncryptor";
private static final String DETECTOR_BEAN_NAME = "lazyEncryptablePropertyDetector";
private static final String CONFIG_SINGLETON = "configPropsSingleton";
static final String RESOLVER_BEAN_NAME = "lazyEncryptablePropertyResolver";
static final String FILTER_BEAN_NAME = "lazyEncryptablePropertyFilter";
// spring 环境副本
@Bean
public EnvCopy envCopy(final ConfigurableEnvironment environment) {
return new EnvCopy(environment);
}
// 字符串解密器, 用于解密加密的字符串, 属性解析器里会用到
@Bean(name = ENCRYPTOR_BEAN_NAME)
public StringEncryptor stringEncryptor(
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") final EnvCopy envCopy,
final BeanFactory bf) {
// 获取 string 解密器的名称, 如果没有 使用默认的名字: jasyptStringEncryptor
final String customEncryptorBeanName = envCopy.get().resolveRequiredPlaceholders(ENCRYPTOR_BEAN_PLACEHOLDER);
return new DefaultLazyEncryptor(envCopy.get(), customEncryptorBeanName, bf);
}
// 可解密属性探测器, 用于发现配置文件中需要被解密的 value
@Bean(name = DETECTOR_BEAN_NAME)
public EncryptablePropertyDetector encryptablePropertyDetector(
@SuppressWarnings({"SpringJavaInjectionPointsAutowiringInspection"}) final EnvCopy envCopy,
final BeanFactory bf) {
// 获取前缀
final String prefix = envCopy.get().resolveRequiredPlaceholders("${jasypt.encryptor.property.prefix:ENC(}");
// 获取后缀
final String suffix = envCopy.get().resolveRequiredPlaceholders("${jasypt.encryptor.property.suffix:)}");
// 获取探测器的 bean 名称, 使用resolveRequiredPlaceholders方法可以获得配置文件中的属性值, 如果没有, 使用默认值: encryptablePropertyDetector
final String customDetectorBeanName = envCopy.get().resolveRequiredPlaceholders(DETECTOR_BEAN_PLACEHOLDER);
return new DefaultLazyPropertyDetector(prefix, suffix, customDetectorBeanName, bf);
}
// jasypt 配置 bean 因为此时配置文件还不能直接使用(有需要被解密的值) 所以不要使用 spring 来注入, 而是从上下文中获取配置, 然后使用 spring 的工具手动的绑定到配置类上
@Bean(name = CONFIG_SINGLETON)
public Singleton<JasyptEncryptorConfigurationProperties> configProps(
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") final EnvCopy envCopy,
final ConfigurableBeanFactory bf) {
return new Singleton<>(() -> {
// 绑定处理器 忽略错误的绑定处理器
final BindHandler handler = new IgnoreErrorsBindHandler(BindHandler.DEFAULT);
// 配置文件
final MutablePropertySources propertySources = envCopy.get().getPropertySources();
// 绑定器
final Binder binder = new Binder(ConfigurationPropertySources.from(propertySources),
new PropertySourcesPlaceholdersResolver(propertySources),
ApplicationConversionService.getSharedInstance(), bf::copyRegisteredEditorsTo);
// jasypt 配置信息
final JasyptEncryptorConfigurationProperties config = new JasyptEncryptorConfigurationProperties();
// 解析类型, 记录了需要解析的 class 一切元信息, 是个很复杂的类, 包含了一个类型的所有 reflect 信息 以及常用的判断函数
final ResolvableType type = ResolvableType.forClass(JasyptEncryptorConfigurationProperties.class);
// 寻找ConfigurationProperties注解信息
final Annotation annotation = AnnotationUtils.findAnnotation(JasyptEncryptorConfigurationProperties.class,
ConfigurationProperties.class);
final Annotation[] annotations = new Annotation[] {annotation};
// 声明一个可绑定类型
final Bindable<?> target = Bindable.of(type).withExistingValue(config).withAnnotations(annotations);
// 开始绑定
binder.bind("jasypt.encryptor", target, handler);
return config;
});
}
// 声明一个 属性过滤器
@SuppressWarnings("unchecked")
@Bean(name = FILTER_BEAN_NAME)
public EncryptablePropertyFilter encryptablePropertyFilter(
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") final EnvCopy envCopy,
final ConfigurableBeanFactory bf,
@Qualifier(CONFIG_SINGLETON) final Singleton<JasyptEncryptorConfigurationProperties> configProps) {
final String customFilterBeanName = envCopy.get().resolveRequiredPlaceholders(FILTER_BEAN_PLACEHOLDER);
// 声明过滤器, 指定包含的资源, 不包含的资源, 包含的属性名, 不包含的属性名
final FilterConfigurationProperties filterConfig = configProps.get().getProperty().getFilter();
return new DefaultLazyPropertyFilter(filterConfig.getIncludeSources(), filterConfig.getExcludeSources(),
filterConfig.getIncludeNames(), filterConfig.getExcludeNames(), customFilterBeanName, bf);
}
// 核心 属性解析器
@Bean(name = RESOLVER_BEAN_NAME)
public EncryptablePropertyResolver encryptablePropertyResolver(
@Qualifier(DETECTOR_BEAN_NAME) final EncryptablePropertyDetector propertyDetector,
@Qualifier(ENCRYPTOR_BEAN_NAME) final StringEncryptor encryptor, final BeanFactory bf,
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") final EnvCopy envCopy) {
final String customResolverBeanName = envCopy.get().resolveRequiredPlaceholders(RESOLVER_BEAN_PLACEHOLDER);
return new DefaultLazyPropertyResolver(propertyDetector, encryptor, customResolverBeanName, bf);
}
/**
* Need a copy of the environment without the Enhanced property sources to avoid circular dependencies.
*/
private static class EnvCopy {
StandardEnvironment copy;
EnvCopy(final ConfigurableEnvironment environment) {
copy = new StandardEnvironment();
environment.getPropertySources().forEach(ps -> {
final PropertySource<?> original = ps instanceof EncryptablePropertySource
? ((EncryptablePropertySource) ps).getDelegate()
: ps;
copy.getPropertySources().addLast(original);
});
}
ConfigurableEnvironment get() {
return copy;
}
}
}
5. 查看 第 3 步中声明的 EnableEncryptablePropertiesBeanFactoryPostProcessor (核心类)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor, ApplicationListener<ApplicationEvent>, Ordered {
private static final Logger LOG = LoggerFactory.getLogger(EnableEncryptablePropertiesBeanFactoryPostProcessor.class);
private ConfigurableEnvironment environment;
private InterceptionMode interceptionMode;
public EnableEncryptablePropertiesBeanFactoryPostProcessor(ConfigurableEnvironment environment, InterceptionMode interceptionMode) {
this.environment = environment;
this.interceptionMode = interceptionMode;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
LOG.info("Post-processing PropertySource instances");
// 获取第 4 步声明的 属性解析器
EncryptablePropertyResolver propertyResolver = beanFactory.getBean(RESOLVER_BEAN_NAME, EncryptablePropertyResolver.class);
// 获取第 4 步声明的 过滤器
EncryptablePropertyFilter propertyFilter = beanFactory.getBean(FILTER_BEAN_NAME, EncryptablePropertyFilter.class);
// 从上下文中拿到配置信息资源
MutablePropertySources propSources = environment.getPropertySources();
// 开始转换资源信息, 这个函数是 EncryptablePropertySourceConverter 类的静态方法, 在文件头部 import 了
convertPropertySources(interceptionMode, propertyResolver, propertyFilter, propSources);
}
...省略的一些代码
}
6. 查看 EncryptablePropertySourceConverter (核心类 真正的解密转换处理类)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public class EncryptablePropertySourceConverter {
// 第 5 步调用的静态方法
public static void convertPropertySources(InterceptionMode interceptionMode, EncryptablePropertyResolver propertyResolver, EncryptablePropertyFilter propertyFilter, MutablePropertySources propSources) {
StreamSupport.stream(propSources.spliterator(), false)
.filter(ps -> !(ps instanceof EncryptablePropertySource)) // 过滤出不是可解密的属性资源对象
.map(ps -> makeEncryptable(interceptionMode, propertyResolver, propertyFilter, ps)) // 把资源对象转换成 EncryptablePropertySource 对象
.collect(toList()) // 组成集合
.forEach(ps -> propSources.replace(ps.getName(), ps)); // 替换属性对象
}
// 生成可解密对象
@SuppressWarnings("unchecked")
public static <T> PropertySource<T> makeEncryptable(InterceptionMode interceptionMode, EncryptablePropertyResolver propertyResolver, EncryptablePropertyFilter propertyFilter, PropertySource<T> propertySource) {
if (propertySource instanceof EncryptablePropertySource) {
return propertySource;
}
// 转换方法 最最最最核心的代码了
PropertySource<T> encryptablePropertySource = convertPropertySource(interceptionMode, propertyResolver, propertyFilter, propertySource);
log.info("Converting PropertySource {} [{}] to {}", propertySource.getName(), propertySource.getClass().getName(),
AopUtils.isAopProxy(encryptablePropertySource) ? "AOP Proxy" : encryptablePropertySource.getClass().getSimpleName());
return encryptablePropertySource;
}
// 转换当前 propertySource 对象为可解密对象
private static <T> PropertySource<T> convertPropertySource(InterceptionMode interceptionMode, EncryptablePropertyResolver propertyResolver, EncryptablePropertyFilter propertyFilter, PropertySource<T> propertySource) {
return interceptionMode == InterceptionMode.PROXY
? proxyPropertySource(propertySource, propertyResolver, propertyFilter) : instantiatePropertySource(propertySource, propertyResolver, propertyFilter);
}
// 对外提供的公有方法, 本类中没用
public static MutablePropertySources proxyPropertySources(InterceptionMode interceptionMode, EncryptablePropertyResolver propertyResolver, EncryptablePropertyFilter propertyFilter, MutablePropertySources propertySources) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(MutablePropertySources.class);
proxyFactory.setProxyTargetClass(true);
proxyFactory.addInterface(PropertySources.class);
proxyFactory.setTarget(propertySources);
proxyFactory.addAdvice(new EncryptableMutablePropertySourcesInterceptor(interceptionMode, propertyResolver, propertyFilter));
return (MutablePropertySources) proxyFactory.getProxy();
}
// 代理属性对象
@SuppressWarnings("unchecked")
public static <T> PropertySource<T> proxyPropertySource(PropertySource<T> propertySource, EncryptablePropertyResolver resolver, EncryptablePropertyFilter propertyFilter) {
//Silly Chris Beams for making CommandLinePropertySource getProperty and containsProperty methods final. Those methods
//can't be proxied with CGLib because of it. So fallback to wrapper for Command Line Arguments only.
// 如果是 CommandLinePropertySource 类型 或者 这个类是 final 的, 那么就不做代理了, 使用 wrapper 代替 ps
if (CommandLinePropertySource.class.isAssignableFrom(propertySource.getClass())
// Other PropertySource classes like org.springframework.boot.env.OriginTrackedMapPropertySource
// are final classes as well
|| Modifier.isFinal(propertySource.getClass().getModifiers())) {
return instantiatePropertySource(propertySource, resolver, propertyFilter);
}
// 代理开始
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetClass(propertySource.getClass());
proxyFactory.setProxyTargetClass(true);
proxyFactory.addInterface(EncryptablePropertySource.class);
proxyFactory.setTarget(propertySource);
proxyFactory.addAdvice(new EncryptablePropertySourceMethodInterceptor<>(propertySource, resolver, propertyFilter));
return (PropertySource<T>) proxyFactory.getProxy();
}
@SuppressWarnings("unchecked")
public static <T> PropertySource<T> instantiatePropertySource(PropertySource<T> propertySource, EncryptablePropertyResolver resolver, EncryptablePropertyFilter propertyFilter) {
PropertySource<T> encryptablePropertySource;
if (needsProxyAnyway(propertySource)) { // 判断 如果是指定的类型, 那么就走代理
encryptablePropertySource = proxyPropertySource(propertySource, resolver, propertyFilter);
} else if (propertySource instanceof MapPropertySource) {
encryptablePropertySource = (PropertySource<T>) new EncryptableMapPropertySourceWrapper((MapPropertySource) propertySource, resolver, propertyFilter);
} else if (propertySource instanceof EnumerablePropertySource) {
encryptablePropertySource = new EncryptableEnumerablePropertySourceWrapper<>((EnumerablePropertySource) propertySource, resolver, propertyFilter);
} else {
encryptablePropertySource = new EncryptablePropertySourceWrapper<>(propertySource, resolver, propertyFilter);
}
return encryptablePropertySource;
}
@SuppressWarnings("unchecked")
private static boolean needsProxyAnyway(PropertySource<?> ps) {
return needsProxyAnyway((Class<? extends PropertySource<?>>) ps.getClass());
}
private static boolean needsProxyAnyway(Class<? extends PropertySource<?>> psClass) {
return needsProxyAnyway(psClass.getName());
}
/**
* Some Spring Boot code actually casts property sources to this specific type so must be proxied.
*/
private static boolean needsProxyAnyway(String className) {
return Stream.of(
"org.springframework.boot.context.config.ConfigFileApplicationListener$ConfigurationPropertySources",
"org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource"
).anyMatch(className::equals);
}
}
7. 至此 jasypt 的核心逻辑分析结束
后续在需要使用 propertySource 的时候, 实际上调用的是 proxy 对象的 getProperty 的方法, 或者 wrapper 的 getProperty 方法
其中使用的核心类为 CachingDelegateEncryptablePropertySource , 这个类继承了 propertySource, 并实现了 EncryptablePropertySource 接口
内部维护了一个 map 缓存, 初始化时, 需要使用 属性解析器 以及 过滤器, 然后使用 解密器解密, 过滤器进行属性过滤
8. 其他类
除了针对配置文件的处理, 还有针对注解的处理: EncryptablePropertySourceBeanFactoryPostProcessor
这个类, 与 EnableEncryptablePropertiesBeanFactoryPostProcessor 类似, 只不过没有实现 applicationListener
它在 postProcessBeanFactory 方法之后, 扫描了注释了 EncryptablePropertySources 的 bean, 并把注解中的配置, 初始化成 ps 对象, 放到上下文中
9. 思路整理:
入口(JasyptSpringBootAutoConfiguration JasyptSpringCloudBootstrapConfiguration)
配置类(EnableEncryptablePropertiesConfiguration), 注册 Bean 类型为: beanFactoryPostProcessor, 在初始化之后调用一些方法
配置类引入了一个属性解析配置类(EncryptablePropertyResolverConfiguration), 类内声明了 字符串解密器(StringEncryptor), 环境上下文副本(EnvCopy), 属性解析器(EncryptablePropertyResolver), 属性过滤器(EncryptablePropertyFilter)
定义的 beanFactoryPostProcessor 在回调中获取: 属性解析器, 属性过滤器, 环境上下文, 然后开始代理属性对象, 后续 spring 调用 propertySource 的 getProperty 实际调用 jasypt 代理的属性对象, 从而获得解密加密能力