Spring3下如何通过Jasypt编程获取解析解密后的属性值?
问题背景
在Spring3中配置Jasypt的EncryptablePropertyPlaceholderConfigurer后,依赖加密属性的Bean能正常获取解密后的值,但调用ApplicationContext.getEnvironment().getProperty("spring.datasource.password")时,要么拿到未解析的占位符字符串${some.encrypted.string},要么返回null,无法获取解密后的实际值。
核心原因
EncryptablePropertyPlaceholderConfigurer是Spring传统PropertyPlaceholderConfigurer的子类,它的工作逻辑是在Bean初始化阶段直接替换Bean定义中的占位符,不会将解析后的解密属性注册到Spring的Environment属性源中,因此从Environment无法直接获取到处理后的属性值。
解决方案
方法1:自定义子类暴露解析后的属性
由于EncryptablePropertyPlaceholderConfigurer的getProperty()方法是protected的,我们可以自定义子类暴露该方法,直接从配置器中获取解析后的解密属性:
- 自定义配置器子类:
import org.jasypt.spring3.properties.EncryptablePropertyPlaceholderConfigurer; public class ExposedEncryptablePropertyConfigurer extends EncryptablePropertyPlaceholderConfigurer { // 暴露获取解析后属性的方法 public String getResolvedProperty(String key) { return super.getProperty(key); } }
- 修改XML配置中的Bean类:
<bean id="propertyPlaceholderConfigurer" class="com.yourpackage.ExposedEncryptablePropertyConfigurer"> <constructor-arg ref="configurationEncryptor"/> <!-- 其他原有配置保持不变 --> <property name="searchSystemEnvironment" value="true"/> <property name="ignoreResourceNotFound" value="true"/> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/> <property name="locations"> <list> <value>file:${spring.config.additional-location}</value> <value>classpath:application.properties</value> </list> </property> </bean>
- 编程获取解密后的值:
final ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("jasypt.xml"); ExposedEncryptablePropertyConfigurer configurer = ctx.getBean(ExposedEncryptablePropertyConfigurer.class); // 获取解密后的属性值 String decryptedPassword = configurer.getResolvedProperty("spring.datasource.password");
方法2:使用Spring3.1+兼容的属性配置器(推荐)
如果你的项目基于Spring3.1及以上版本,可以使用Jasypt提供的EncryptablePropertySourcesPlaceholderConfigurer,它会将解析后的解密属性注册到Environment的属性源中,这样就能直接通过Environment获取值:
- 修改XML配置中的Bean类:
<bean id="propertyPlaceholderConfigurer" class="org.jasypt.spring31.properties.EncryptablePropertySourcesPlaceholderConfigurer"> <constructor-arg ref="configurationEncryptor"/> <!-- 其他原有配置保持不变 --> <property name="searchSystemEnvironment" value="true"/> <property name="ignoreResourceNotFound" value="true"/> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/> <property name="locations"> <list> <value>file:${spring.config.additional-location}</value> <value>classpath:application.properties</value> </list> </property> </bean>
- 直接从Environment获取解密后的值:
final ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("jasypt.xml"); String decryptedPassword = ctx.getEnvironment().getProperty("spring.datasource.password");
方法3:手动解密(不推荐,繁琐)
如果上述方法都无法使用,可以手动获取加密器,自行处理占位符和解密逻辑:
final ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("jasypt.xml"); StandardPBEStringEncryptor encryptor = ctx.getBean(StandardPBEStringEncryptor.class); // 先获取spring.datasource.password对应的占位符内容 String placeholder = ctx.getEnvironment().getProperty("spring.datasource.password"); if (placeholder != null && placeholder.startsWith("${") && placeholder.endsWith("}")) { // 提取占位符中的属性key String propertyKey = placeholder.substring(2, placeholder.length() - 1); // 获取原始加密字符串 String encryptedValue = ctx.getEnvironment().getProperty(propertyKey); if (encryptedValue != null && encryptedValue.startsWith("ENC(") && encryptedValue.endsWith(")")) { // 去掉ENC()包裹,解密 String rawEncrypted = encryptedValue.substring(4, encryptedValue.length() - 1); String decryptedPassword = encryptor.decrypt(rawEncrypted); return decryptedPassword; } } return null;
版本注意事项
- Spring3.0.x版本:只能使用方法1或方法3;
- Spring3.1+版本:优先使用方法2,更符合Spring环境属性管理的设计。
内容的提问来源于stack exchange,提问作者user3258911

