其实这是一个比较篇实战的一个问题,我们在application.yml中保存的MySQL数据库的账号密码或者其他服务的账号密码,都可以保存加密后的内容,那么我们在处理的时候要怎么解密呢?这个其实比较简单只需要对SpringBoot的执行流程清楚就可以了,第一个我们可以通过自定义监听器可以在加载解析了配置文件之后对加密的文件中做解密处理同时覆盖之前加密的内容,或者通过对应的后置处理器来处理,具体的实现如下:
首先我们在属性文件中配置加密后的信息
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mb?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
# 对通过3DES对密码加密
spring.datasource.password=t5Jd2CzFWEw=
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.mapper-locations=classpath:mapper/*.xml
在SpringBoot项目启动的时候在在刷新Spring容器之前执行的,所以我们要做的就是在加载完环境配置信息后,获取到配置的 spring.datasource.password=t5Jd2CzFWEw=
这个信息,然后解密并修改覆盖就可以了。
然后在属性文件的逻辑其实是通过发布事件触发对应的监听器来实现的
所以第一个解决方案就是你自定义一个监听器,这个监听器在加载属性文件(ConfigFileApplicationListener)的监听器之后处理,这种方式稍微麻烦点。
3DES的工具类
package com.dist.ytgz.approve.listener;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.net.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
/**
* 3DES加密算法,主要用于加密用户id,身份证号等敏感信息,防止破解
*/
public class DESedeUtil {
//秘钥
public static final String KEY = "~@#$y1a2n.&@+n@$%*(1)";
//秘钥长度
private static final int secretKeyLength = 24;
//加密算法
private static final String ALGORITHM = "DESede";
//编码
private static final String CHARSET = "UTF-8";
/**
* 转换成十六进制字符串
* @param key
* @return
*/
public static byte[] getHex(String key){
byte[] secretKeyByte = new byte[24];
try {
byte[] hexByte;
hexByte = new String(DigestUtils.md5Hex(key)).getBytes(CHARSET);
//秘钥长度固定为24位
System.arraycopy(hexByte,0,secretKeyByte,0,secretKeyLength);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return secretKeyByte;
}
/**
* 生成密钥,返回加密串
* @param key 密钥
* @param encodeStr 将加密的字符串
* @return
*/
public static String encode3DES(String key,String encodeStr){
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(getHex(key), ALGORITHM));
return Base64.encodeBase64String(cipher.doFinal(encodeStr.getBytes(CHARSET)));
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/**
* 生成密钥,解密,并返回字符串
* @param key 密钥
* @param decodeStr 需要解密的字符串
* @return
*/
public static String decode3DES(String key, String decodeStr){
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(getHex(key),ALGORITHM));
return new String(cipher.doFinal(new Base64().decode(decodeStr)),CHARSET);
} catch(Exception e){
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String pwd = "root";
String encode = DESedeUtil.encode3DES(KEY, pwd);
String decode = DESedeUtil.decode3DES(KEY, encode);
System.out.println("用户id>>>"+pwd);
System.out.println("用户id加密>>>"+encode);
System.out.println("用户id解密>>>"+decode);
}
}
自定义监听器
package com.dist.ytgz.approve.listener;
import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.Ordered;
/**
* TODO
*
* @author Zhang Xiao
* @since
*/
public class AfterConfigListener implements SmartApplicationListener, Ordered {
// 复制的ConfigFileApplicationListener文件内容
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType) || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
}
public void onApplicationEvent(ApplicationEvent event) {
// ApplicationEnvironmentPreparedEvent 是加载配置文件,初始化日志系统的事件。
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 获得原来的password内容
String password = ((ApplicationEnvironmentPreparedEvent) event).getEnvironment().getProperty("spring.datasource.password");
// 进行密码的解密
System.setProperty("spring.datasource.password", DESedeUtil.decode3DES(DESedeUtil.KEY, password));
}
if (event instanceof ApplicationPreparedEvent) {
}
}
@Override
public int getOrder() {
// 设置该监听器 在加载配置文件之后执行
return ConfigFileApplicationListener.DEFAULT_ORDER + 1;
}
}
启动类
package com.dist.ytgz.approve;
import com.dist.ytgz.approve.listener.AfterConfigListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4J;
import uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4JServletContextListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* TODO
*
* @author Zhang Xiao
* @since
*/
@SpringBootApplication
public class DistApproveApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(DistApproveApplication.class);
springApplication.addListeners(new AfterConfigListener());
springApplication.run(args);
}
}
第二种方式就是通过加载属性文件的一个后置处理器来处理,这就已下面例子为例来实现
声明后置处理器
package com.dist.ytgz.approve.listener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;
public class SafetyEncryptProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
for (PropertySource<?> propertySource : environment.getPropertySources()) {
System.out.println("propertySource = " + propertySource);
if(propertySource instanceof OriginTrackedMapPropertySource){
OriginTrackedMapPropertySource source = (OriginTrackedMapPropertySource) propertySource;
for (String propertyName : source.getPropertyNames()) {
//System.out.println(propertyName + "=" + source.getProperty(propertyName));
if("spring.datasource.password".equals(propertyName)){
Map<String,Object> map = new HashMap<>();
// 做解密处理
String property = (String) source.getProperty(propertyName);
String s = DESedeUtil.decode3DES(DESedeUtil.KEY, property);
System.out.println("密文:" + property);
System.out.println("解密后的:" + s);
map.put(propertyName,s);
// 注意要添加到前面,覆盖
environment.getPropertySources().addFirst(new MapPropertySource(propertyName,map));
}
}
}
}
}
}
然后在META-INF/spring.factories文件中注册
org.springframework.boot.env.EnvironmentPostProcessor=com.dist.ytgz.approve.listener.SafetyEncryptProcessor
启动类
package com.dist.ytgz.approve;
import com.dist.ytgz.approve.listener.AfterConfigListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4J;
import uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4JServletContextListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* TODO
*
* @author Zhang Xiao
* @since
*/
@SpringBootApplication
public class DistApproveApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DistApproveApplication.class, args);
}
}