spring、spring-boot配置文件属性内容加解密

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 实际项目开发过程中,我们的应用程序都有很多的配置文件(例如properties或者yml文件等),我们时常会遇到需要对配置文件敏感字段的参数内容进行加密处理(比如数据库连接密码、与第三方的通信密钥等)。

实际项目开发过程中,我们的应用程序都有很多的配置文件(例如properties或者yml文件等),我们时常会遇到需要对配置文件敏感字段的参数内容进行加密处理(比如数据库连接密码、与第三方的通信密钥等)。

如果采用一定采用传统的springMVC做系统集成,我们可以继承PropertyPlaceholderConfigurer类并复写其converProperty方法,在该方法内一般需要做两步处理:

  • 1、根据参数名propertyName或者根据参数值propertyValue判断当前是否需要进行内容解密。
  • 2、如果需要解密,根据系统设计调用解密处理逻辑,然后调用父级converProperty方法。

按照上面的思路,我们先实现自己的PropertyPlaceholderConfigurer子类,假如当前我们的需求是要将test.content参数值后面追加内容“《我是后加的内容》”

@Slf4j
public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
    @Override
    protected String convertProperty(String propertyName, String propertyValue) {
        //这里做对属性的内容的加解密等操作
        //TODO something
        if (propertyName.equalsIgnoreCase("test.content")) {
            log.info("当前即将过滤的内容[" + propertyName + "]=[" + propertyValue + "]");
            propertyValue = propertyValue + "《我是后加的内容》";
        } else if (propertyName.equalsIgnoreCase("spring.datasource.password")) {
            propertyValue = propertyValue.replace("abc", "");
        }
        return super.convertProperty(propertyName, propertyValue);
    }
}

接下来,我们要是实例化我们自定义的PropertyPlaceholderConfigurer,如果我们使用xml方式配置,则代码如下:

<bean id="propertyConfigurer" class="com.spring.boot.test.util.MyPropertPlaceholderConfigurer">
    <property name="locations">
        <list>
        <value>classpath:application.poperties</value>
        </list>
    </property>
</bean>

如果采用javaconfig方式时代码如下:

    @Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(){
    PropertyPlaceholderConfigurer placeholderConfigurer=new MyPropertyPlaceholderConfigurer();
    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource resource = resolver.getResource("classpath:application.properties");
    placeholderConfigurer.setLocation(resource);
    return placeholderConfigurer;
}

通过上面的步骤,我们可以在程序的TestService中使用自动注入或者Spring表达式@Value(“$(参数名)”)获取到的内容就是我们解密之后的内容。我们启动程序后我们可以看到日志内容

c.s.b.t.u.MyPropertyPlaceholderConfigurer[16] - 当前即将过滤的内容[test.content]=[测试内容]
c.s.b.t.s.TestService[24] - 当前拿到的testContent=测试内容《我是后加的内容》

遗憾的时,上述方式存在两个缺点:

  • 1、目前只支持properties配置文件,当我们使用yaml文件后就不生效了,运行会报文件格式异常,如果要生效,必须复写底层的loadProperties方法分别对不同格式的文件进行解析。但是这样可能会麻烦的多。
  • 2、当我们使用spring boot后,对数据库相关配置参数解析后,数据库自动初始化装配无法成功,即数据库会在PropertyPlaceholderConfigurer类之前初始化,如果加密内容正好是数据库连接密码,那么程序启动后会因为数据库无法连接而报错,程序自动挂断。错误内容:Failed to initialize pool: Access denied for user 'root'@'localhost' (using password: YES)

面对以上问题,直接通过修改PropertyPlaceholderConfigurer解决的路子我并没有去测试,不过接下来我们介绍另外一种方式解决这个问题,那就是spring boot集成jasypt框架实现对配置文件的参数内容加解密。

jasypt是一个java实现的安全框架,最新版本已经不提供PlaceholderConfigurer,所以我们很难直接使用该框架报对properties或者yml文件进行处理,但是国外大神Ulises Bocchio写了一个spring boot下用的工具包github地址https://github.com/ulisesbocchio/jasypt-spring-boot,该工具包支持使用jasypt框架来处理properties和yml配置文件参数内容的加解密操作,该工具已经发布到了中央仓库供大家使用。而且文档信息非常详细。下面我们简单说一下该工具的优势。

  • 1、该工具支持注解方式开启jasypt功能,以及注解方式引入一个或多个需要处理的配置文件。
  • 2、该工具同时支持properties与yml文件的解析处理。
  • 3、该工具支持自定义加解密类型和复写加解密方法。

在早期的版本中jasypt为spring提供过EncryptablePropertyPlaceholderConfigurer与EncryptablePropertySourcesPlaceholderConfigurer两个类,但是后面的包中都不包含这两个类了。如果希望使用这种方式可以参考如下两篇博客:

最新的1.9.2版本中以上两个类都已经不存在了。所以推荐使用Ulises Bocchio写工具包来做系统集成。下面我们按照其文档简单配置一下使用思路。

首先,引入工具包依赖

<dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
        <version>1.14</version>
</dependency>

如果不使用stater,我们可以引入如下包

<dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot</artifactId>
        <version>1.14</version>
</dependency>

默认情况下jasypt采用的算法是PBEWithMD5AndDES,该算法对同一串明文每次加密的密文都不一样,比较适合做数据加解密。但是该算法必须配置密码,我们在yml文件配置如下参数

jasypt:
  encryptor:
    password: e!Jd&ljyJ^e4I5oU

如果想要改变其他配置例如密文的前后缀也可以在这里配置。

如果上面引入的包不是starter,那么需要在启动类上添加注解@EnableEncryptableProperties以启动该功能。

然后我们写一个junit测试类,具体内容如下

public class TestBootTest {
    @Autowired
    StringEncryptor stringEncryptor;//密码解码器自动注入

    @Test
    public void test() {
        System.out.println(stringEncryptor.encrypt("123456"));
    }
}

执行后的控制台输出123456加密后的内容:P7xVJnbrn/MCzyVEOejTRw==

小提示:在控制台下,我们直接使用jasypt工具包进行原始数据加密可以采用如下命令

java -cp jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input=原文 password=密码 algorithm=加密方式

例如,我们要对中文“原文”进行加密,密码为123456,加密方式为PBEWithMD5AndDES,则具体命令为

java -cp jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input=原文 password=123456 algorithm=PBEWithMD5AndDES

执行后会在控制台输出如下内容

----ENVIRONMENT-----------------
Runtime: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 24.79-b02
----ARGUMENTS-------------------
algorithm: PBEWithMD5AndDES
input: 原文
password: 123456
----OUTPUT----------------------
BDhzghe2quZtDpX0ZM/e4w==

我们将spring.datasource.password的值配置为ENC(P7xVJnbrn/MCzyVEOejTRw==),为什么需要这么配置呢,因为默认情况下jasypt-spring-boot解析密码的规则是前缀是ENC(后缀是)。我们改造我们的单元测试工具类

public class TestBootTest {
    @Autowired
    StringEncryptor stringEncryptor;//密码解码器自动注入
    @Value("${spring.datasource.password}")
    private String password;
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Test
    public void test() {
//        System.out.println(stringEncryptor.encrypt("123456"));
        System.out.println("连接数据库密码:" + password);
        System.out.println("查询到的信息:"+userInfoMapper.selectByUserId(1));
    }
}

启动单元测试,可以看到日志输出了如下内容

连接数据库密码:123456
[2017-08-03 22:20:41,653][INFO ] c.z.h.HikariDataSource[95] - HikariPool-1 - Starting...
[2017-08-03 22:20:42,200][INFO ] c.z.h.HikariDataSource[107] - HikariPool-1 - Start completed.
查询到的信息:UserInfo(id=1, userName=test1, userAge=20, address=四川成都, addTime=2016-06-05 19:32:42)

可以看到采用jasypt-spring-boot-starter工具来集成spring boot与jasypt的功能成功实现了。我们看再看下配置日志,可以看到默认情况下字符串加密机默认的配置信息

[2017-08-03 22:20:32,008][INFO ] c.u.j.StringEncryptorConfiguration[45] - Encryptor config not found for property jasypt.encryptor.algorithm, using default value: PBEWithMD5AndDES
[2017-08-03 22:20:32,009][INFO ] c.u.j.StringEncryptorConfiguration[45] - Encryptor config not found for property jasypt.encryptor.keyObtentionIterations, using default value: 1000
[2017-08-03 22:20:32,009][INFO ] c.u.j.StringEncryptorConfiguration[45] - Encryptor config not found for property jasypt.encryptor.poolSize, using default value: 1
[2017-08-03 22:20:32,010][INFO ] c.u.j.StringEncryptorConfiguration[45] - Encryptor config not found for property jasypt.encryptor.providerName, using default value: null
[2017-08-03 22:20:32,012][INFO ] c.u.j.StringEncryptorConfiguration[45] - Encryptor config not found for property jasypt.encryptor.saltGeneratorClassname, using default value: org.jasypt.salt.RandomSaltGenerator
[2017-08-03 22:20:32,029][INFO ] c.u.j.StringEncryptorConfiguration[45] - Encryptor config not found for property jasypt.encryptor.stringOutputType, using default value: base64

通过以上方式使用jasypt框架处理配置文件参数加密的功能就基本上OK了,但是存在一定的风险,那就是程序配置文件中,存在解密密文的密码。因为PBEWithMD5AndDES算法到处都可以找到实现。如果拿到了数据库密文和算法的密码,那么很容易解析出连接数据库的密码。一般严谨的做法是不会将密文信息与解密工具放在一起,避免程序被获取后,加密算法和数据库密码密文以及解密密码都同时被泄露。我们公司就是采用C语言写了一个加解密工具,放在服务器上特定的位置。有Java程序中去调用该工具进行解密。那么这种情况下我们怎么自定义解密逻辑呢?

从单元测试中可以看到我们注入了一个加解密的接口StringEncryptor,跟踪改类的实现可以看到系统调用了encrypt方法用于明文加密,调用decrypt对密文进行解密。于是我们实现该接口,假如我们将数据库密码参数spring.datasource.password=123456配置为spring.datasource.password=ENC(654321),那么在该类里面我们要将654321替换为123456,得到我们的真实密码。代码如下

public class DefaultEncryptor implements StringEncryptor {
    @Override
    public String encrypt(String message) {
        if ("123456".equalsIgnoreCase(message)) {
            message = "654321";
        }
        return message;
    }

    @Override
    public String decrypt(String encryptedMessage) {
        if ("654321".equalsIgnoreCase(encryptedMessage)) {
            log.info("将密文[654321]替换为[123456]");
            encryptedMessage = "123456";
        }
        return encryptedMessage;
    }
}

接下来,我们使用java config方式实例化该对象替换默认的StringEncryptor实例,如下

@Bean(name = "stringEncryptor")
public StringEncryptor stringEncryptor() {
    return new DefaultEncryptor();
}

我们运行单元测试,可以看到自定义密码解码器的日志信息

[2017-08-03 22:31:47,291][INFO ] c.s.b.t.u.DefaultEncryptor[22] - 将密文[654321]替换为[123456]

通过以上步骤,我们采用自定义的StringEncryptor实现了上下文中敏感信息的自定义加解密处理。
特别说明,文中引入的jasypt-spring-boot-starter的版本是1.14,该版本要求JRE版本必须为1.8。如果要想在1.6或者1.7下运行,那么必须使用1.4或1.5的版本的定制版本。

1.7版本下引入:

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>1.5-java7</version>
</dependency>

1.6版本下引入:

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>1.5-java6</version>
</dependency>

参考:

https://github.com/ulisesbocchio/jasypt-spring-boot

相关文章
|
13天前
|
XML Java 数据格式
Spring从入门到入土(xml配置文件的基础使用方式)
本文详细介绍了Spring框架中XML配置文件的使用方法,包括读取配置文件、创建带参数的构造对象、使用工厂方法和静态方法创建对象、对象生命周期管理以及单例和多例模式的测试。
59 7
Spring从入门到入土(xml配置文件的基础使用方式)
|
2月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
11天前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
22 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
9天前
|
Java 测试技术 Spring
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
这篇文章介绍了Spring Boot中配置文件的语法、如何读取配置文件以及如何通过静态工具类读取配置文件。
16 0
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
|
23天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
42 2
|
1月前
|
消息中间件 NoSQL 安全
(转)Spring Boot加载 不同位置的 application.properties配置文件顺序规则
这篇文章介绍了Spring Boot加载配置文件的顺序规则,包括不同位置的application.properties文件的加载优先级,以及如何通过命令行参数或环境变量来指定配置文件的名称和位置。
|
2月前
|
Java Spring 传感器
AI 浪潮席卷,Spring 框架配置文件管理与环境感知,为软件稳定护航,你还在等什么?
【8月更文挑战第31天】在软件开发中,配置文件管理至关重要。Spring框架提供强大支持,便于应对不同环境需求,如电商项目的开发、测试与生产环境。它支持多种格式的配置文件(如properties和YAML),并能根据环境加载不同配置,如数据库连接信息。通过`@Profile`注解可指定特定环境下的配置生效,同时支持通过命令行参数或环境变量覆盖配置值,确保应用稳定性和可靠性。
51 0
|
2月前
|
Java Spring 开发者
Spring 框架配置属性绑定大比拼:@Value 与 @ConfigurationProperties,谁才是真正的王者?
【8月更文挑战第31天】Spring 框架提供 `@Value` 和 `@ConfigurationProperties` 两种配置属性绑定方式。`@Value` 简单直接,适用于简单场景,但处理复杂配置时略显不足。`@ConfigurationProperties` 则以类级别绑定配置,简化代码并更好组织配置信息。本文通过示例对比两者特点,帮助开发者根据具体需求选择合适的绑定方式,实现高效且易维护的配置管理。
46 0
|
2月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
41 0
|
2月前
|
XML Java 数据库连接
深入解析 Spring 配置文件:从基础到高级
【8月更文挑战第3天】Spring配置文件是构建与管理Spring应用的核心,它涵盖了从基础到高级的各种配置技巧。基础配置采用`.xml`格式定义Bean及其依赖;中级配置包括设置Bean作用域及引入属性文件;高级配置则涉及AOP、事务管理和与其他框架的整合。熟练掌握这些配置能帮助开发者构建出更为灵活且易维护的应用系统。