手机用户请
横屏
获取最佳阅读体验,REFERENCES
中是本文参考的链接,如需要链接和更多资源,可以关注其他博客发布地址。
平台 | 地址 |
---|---|
CSDN | https://blog.csdn.net/sinat_28690417 |
简书 | https://www.jianshu.com/u/3032cc862300 |
个人博客 | https://yiyuery.github.io/NoteBooks/ |
继上篇文章《[Spring Boot] 配置文件加载[超详细]》之后,今天来介绍个好用的工具
jasypt-spring-boot-starter
。 主要用途是可以实现配置文件的加密,避免一些敏感信息泄露。也无需自定义加解密工具,集成Spring Boot
,轻量好用。
jasypt-spring-boot-starter
介绍Jasypt Spring Boot为Spring Boot Applications中的属性源提供加密支持。 有三种方法可以集成jasypt-spring-boot到您的项目中:
jasypt-spring-boot-starter
如果使用@SpringBootApplication
或@EnableAutoConfiguration
将在整个Spring环境中启用加密属性,只需将该jar添加到类路径中即可jasypt-spring-boot
到类路径并添加@EnableEncryptableProperties
到主Configuration
类以在整个Spring环境中启用可加密属性jasypt-spring-boot
到类路径并使用声明单个可加密属性源@EncrytablePropertySource
更新
更新1/8/2019:版本2.1.1版本包括非对称加密
和支持带有IV生成器的JSB96(感谢@melloware !!)
更新7/17/2018:版本2.1.0发行版包括过滤器
更新3/17/2018:已发布支持Spring Boot 2.0.X.RELEASE的2.0.0版。SemVer采用。
2015年7月18日更新:jasypt-spring-boot现在在Maven Central!
根据上文提到的三种方式分别进行介绍配置方式:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot</artifactId>
<version>2.1.1</version>
</dependency>
然后添加@EnableEncryptableProperties到Configuration类。例如:
@Configuration
@EnableEncryptableProperties
public class MyApplication {
...
}
并且将在整个Spring环境中启用可加密属性(这意味着任何系统属性,环境属性,命令行参数,application.properties,yaml属性和任何其他自定义属性源都可以包含加密属性)
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot</artifactId>
<version>2.0.0</version>
</dependency>
然后@EncryptablePropertySource在配置文件中添加任意数量的注释。就像使用Spring的@PropertySource注释一样。例如:
@Configuration
@EncryptablePropertySource(name = "EncryptedProperties", value = "classpath:encrypted.properties")
public class MyApplication {
...
}
更方便的是,还有一个@EncryptablePropertySources注释,可以用来对类型的注释进行分组,@EncryptablePropertySource如下所示:
@Configuration
@EncryptablePropertySources({@EncryptablePropertySource("classpath:encrypted.properties"),
@EncryptablePropertySource("classpath:encrypted2.properties")})
public class MyApplication {
...
}
另请注意,从1.8版开始,@EncryptablePropertySource支持YAML文件。
环境准备
引入关键依赖,对数据库连接的敏感信息进行加密:
.
buildscript {
repositories {
maven { url = "https://plugins.gradle.org/m2/" }
maven { url = "http://maven.aliyun.com/nexus/content/groups/public/" }
jcenter()
}
dependencies {
classpath libs["spring-boot-gradle-plugin"]
}
}
apply plugin: "io.spring.dependency-management"
apply plugin: "org.springframework.boot"
bootJar {
baseName = 'common-boot-support'
version = '1.0.0'
}
configurations {
all*.exclude group: 'javax.persistence',module:'persistence-api'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compile libs["jasypt-spring-boot-starter"]
runtimeOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
注入工具类实例org.jasypt.util.password.PasswordEncryptor
.
选一种加密实现类即可,比如:
@Configuration
public class SecurityConfiguration {
@Bean
public StandardPBEStringEncryptor passwordEncryptor(){
return new StandardPBEStringEncryptor();
}
}
yaml 中配置密钥
jasypt:
encryptor:
password: xcy
在测试类中注入该实例,编写测试用例获取加密后密文
.
我们随便定义个属性password
添加到yaml中,然后编写接口获取。
password: ENC(2y6fenecYr195Fr38uDxjw==)
此处注意要用ENC()包裹生成的加密后的密码,该规则可以自定义配置
@SpringBootApplication
@ComponentScan("com.example")
@RestController
@RequestMapping("/test")
public class CommonBootSupportApplication {
@Resource
private Environment environment;
public static void main(String[] args) {
SpringApplication.run(CommonBootSupportApplication.class);
}
@GetMapping("/prop")
public String getProp(String key){
return environment.getProperty(key);
}
}
来我们调用接口获取下看看
利用IDEA自带的脚本工具进行测试:
.
GET http://localhost:8080/test/prop?key=password
Accept: application/json
###
.
从图中我们可以看出接口获取的password
字段已经直接解密了。
但是有的同学肯定会说,你这个密钥都放在yaml里,别人取到源码自己执行下不就可以知道你密码了,那么前一篇文章《[Spring Boot] 配置文件加载[超详细]》不正为这个做铺垫么,我们只需要将密钥存放到服务器安全目录下,然后从外部读取即可。
以实际项目部署的数据库敏感信息为例,关键配置列出以供参考:
spring:
datasource:
username: ENC(0oZozS+SZ4qSex4/gKan/w==)
password: ENC(EoUYXgHaCxs3+aiBdofIsbYuvdVKVCQZ)
driver-class-name: com.mysql.cj.jdbc.Driver
url: ENC(0lvWVTKgrpX2CHRuU1ly+NDOd4lVsVSmJSYDsRXF7n0pmPHQhyKK/MaMq1yC1VWEO8pWlVp5lEYiNq2oBh8NLP4QZGNQLQZH8yUN6gZtsYby5Ztvew66sL6LiDNpvbnr)
jpa:
hibernate:
ddl-auto: update
show-sql: true
database-platform: org.hibernate.dialect.MySQL5Dialect
jasypt:
encryptor:
bean: capStringEncryptor
然后密钥存储配置解析和配置加载,此处还需注意该jar包读取的资源的是application.yaml下的,所以需要重写下加密方法的生成类
@Configuration
public class SecurityConfiguration {
@Resource
private Environment environment;
@Resource
private BeanFactory beanFactory;
@Bean(name = "capStringEncryptor")
public PBEStringCleanablePasswordEncryptor capStringEncryptor(){
return CapLazyEncryptor.getSingleton(environment,beanFactory);
}
}
/*
* @ProjectName: 编程学习
* @Copyright: 2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
* @address: https:yiyuery.club
* @date: 2019/5/20 20:57
* @email: xiazhaoyang@live.com
* @description: 本内容仅限于编程技术学习使用,转发请注明出处.
*/
package com.example.common.spring.security;
import com.example.common.spring.config.CapPropertyPlaceholderConfigurer;
import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PBEStringCleanablePasswordEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.env.Environment;
import java.util.Objects;
/**
* <p>
* 自定义加密工具生成类
* 支持外部参数加载
* </p>
*
* @author xiazhaoyang
* @version v1.0.0
* @date 2019-07-28 14:13
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify By: {修改人} 2019-07-28
* @modify reason: {方法名}:{原因}
* ...
*/
@Slf4j
public class CapLazyEncryptor implements StringEncryptor {
private static PBEStringCleanablePasswordEncryptor singleton;
private static CapPropertyPlaceholderConfigurer configurer;
public static synchronized PBEStringCleanablePasswordEncryptor getSingleton(final Environment e, final BeanFactory bf) {
if (Objects.isNull(singleton)) {
singleton = createDefault(e, bf);
}
return singleton;
}
private CapLazyEncryptor() {
}
private static PBEStringCleanablePasswordEncryptor createDefault(Environment e, BeanFactory bf) {
configurer = bf.getBean(CapPropertyPlaceholderConfigurer.class);
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
//密钥获取位置代码修改
config.setPassword(getRequiredProperty(e, "jasypt.encryptor.password"));
config.setAlgorithm(getProperty(e, "jasypt.encryptor.algorithm", "PBEWithMD5AndDES"));
config.setKeyObtentionIterations(getProperty(e, "jasypt.encryptor.keyObtentionIterations", "1000"));
config.setPoolSize(getProperty(e, "jasypt.encryptor.poolSize", "1"));
config.setProviderName(getProperty(e, "jasypt.encryptor.providerName", null));
config.setProviderClassName(getProperty(e, "jasypt.encryptor.providerClassName", null));
config.setSaltGeneratorClassName(getProperty(e, "jasypt.encryptor.saltGeneratorClassname", "org.jasypt.salt.RandomSaltGenerator"));
config.setStringOutputType(getProperty(e, "jasypt.encryptor.stringOutputType", "base64"));
encryptor.setConfig(config);
return encryptor;
}
private static String getProperty(Environment environment, String key, String defaultValue) {
if (!propertyExists(environment, key)) {
log.info("Encryptor config not found for property {}, using default value: {}", key, defaultValue);
}
String property = environment.getProperty(key);
return Objects.isNull(property) ? configurer.getCapProps().getProperty(key, defaultValue) : property;
}
private static boolean propertyExists(Environment environment, String key) {
return environment.getProperty(key) != null || configurer.getCapProps().getProperty(key) != null;
}
private static String getRequiredProperty(Environment environment, String key) {
if (!propertyExists(environment, key)) {
throw new IllegalStateException(String.format("Required Encryption configuration property missing: %s", key));
}
String property = environment.getProperty(key);
//密钥获取位置代码修改,取不到的话从外部配置读取
return Objects.isNull(property) ? configurer.getCapProps().getProperty(key) : property;
}
/**
* Encrypt the input message
*
* @param message the message to be encrypted
* @return the result of encryption
*/
@Override
public String encrypt(String message) {
return singleton.encrypt(message);
}
/**
* Decrypt an encrypted message
*
* @param encryptedMessage the encrypted message to be decrypted
* @return the result of decryption
*/
@Override
public String decrypt(String encryptedMessage) {
return singleton.decrypt(encryptedMessage);
}
}
当然,外部配置文件的加载配置少不了:
/**
* 加载外部配置文件
* @return
*/
@Bean
@Primary
public CapPropertyPlaceholderConfigurer propertyConfigurer(){
CapPropertyPlaceholderConfigurer configurer = new CapPropertyPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
//此处替换为自身项目安全敏感信息存放目录即可
yaml.setResources(new FileSystemResource(Paths.get(RunnerMonitor.getEnvConfPath().getConfPath(), "config.yaml").toFile()));
Properties props = new Properties();
props.putAll(Objects.requireNonNull(yaml.getObject()));
configurer.setProperties(props);
configurer.setFileEncoding(BasicConstants.DEFAULT_ENCODING);
return configurer;
}
好了,心动不如行动,想优雅得给配置中的敏感信息加密就快来实战吧。
本文介绍了一款集成Spring的配置文件优雅加密的工具,并提供了一种外部密钥存储的加密方案。其实除了本文介绍的,该工具的能力还远远不止这些,更多的详见REFERENCES中的GITHUB链接哦!