终于把Apollo存储加密这件事搞定了
本文摘自于《Spring Cloud微服务 入门 实战与进阶》一书。
一些比较重要的配置信息,比如密码之类的敏感配置,我们希望将配置加密存储,保证安全性。Apollo框架本身没有提供数据加密的功能,如果想要实现数据加密的功能有两种方式,第一种是改Apollo的源码,增加加解密的逻辑,第二种比较简单,基于第三方的框架来对数据进行解密。
jasypt-spring-boot是一个基于Spring Boot开发的框架,可以将properties中加密的内容自动解密,在Apollo中也可以借助于jasypt-spring-boot这个框架来实现数据的加解密操作。
jasypt-spring-boot GitHub地址:https://github.com/ulisesbocchio/jasypt-spring-boot
将我们需要加密的配置通过jasypt-spring-boot提供的方法进行加密,然后将加密的内容配置在Apollo中,当项目启动的时候,jasypt-spring-boot会将Apollo加密的配置进行解密,从而让使用者获取到解密之后的内容。
创建一个新的Maven项目,加入Apollo和jasypt的依赖:
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.1.0</version>
</dependency>
<!--jasypt加密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>1.16</version>
</dependency>
加入下面的依赖信息:
server.port=8081
app.id=SampleApp
apollo.meta=http://localhost:8080
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application
jasypt.encryptor.password=yinjihaunkey
- jasypt.encryptor.password:配置加密的Key
创建一个加密的工具类,用于加密配置:
public class EncryptUtil {
/**
* 制表符、空格、换行符 PATTERN
*/
private static Pattern BLANK_PATTERN = Pattern.compile("\\s*|\t|\r|\n");
/**
* 加密Key
*/
private static String PASSWORD = "yinjihaunkey";
/**
* 加密算法
*/
private static String ALGORITHM = "PBEWithMD5AndDES";
public static Map<String, String> getEncryptedParams(String input) {
//输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
PrintStream cacheStream = new PrintStream(byteArrayOutputStream);
//更换数据输出位置
System.setOut(cacheStream);
//加密参数组装
String[] args = {"input=" + input, "password=" + PASSWORD, "algorithm=" + ALGORITHM};
JasyptPBEStringEncryptionCLI.main(args);
//执行加密后的输出
String message = byteArrayOutputStream.toString();
String str = replaceBlank(message);
int index = str.lastIndexOf("-");
//返回加密后的数据
Map<String, String> result = new HashMap<String, String>();
result.put("input", str.substring(index + 1));
result.put("password", PASSWORD);
return result;
}
/**
* 替换制表符、空格、换行符
*
* @param str
* @return
*/
private static String replaceBlank(String str) {
String dest = "";
if (!StringUtils.isEmpty(str)) {
Matcher matcher = BLANK_PATTERN.matcher(str);
dest = matcher.replaceAll("");
}
return dest;
}
public static void main(String[] args) {
System.out.println(getEncryptedParams("hello"));
}
}
执行main方法,可以得到如下输出:
{input=0JK4mrGjPUxkB4XuqEv2YQ==, password=yinjihaunkey}
input就是hello加密之后的内容,将input的值复制存储到Apollo中,存储的格式需要按照一定的规则才行:
test.input = ENC(0JK4mrGjPUxkB4XuqEv2YQ==)
需要将加密的内容用ENC包起来,这样jasypt才会去解密这个值。
使用的地方可以直接根据名称注入配置,比如:
@Value("${test.input}")
private String input;
input的值就是解密之后的值,使用者不需要关心解密逻辑,jasypt框架在内部处理好了。
jasypt整合Apollo也是有一些不足的地方,目前我只发现了下面几个问题:
在配置中心修改值后,项目中的值不会刷新
注入Config对象获取的值无法解密
@ApolloConfig
private Config config;
@GetMapping("/config/getUserName3")
public String getUserName3() {
return config.getProperty("test.input", "yinjihuan");
}
上面列举的2个问题,跟jasypt的实现方式是有关系的,意味着这种加密的方式可能只适合数据库密码之类的,启动时是可以解密的,而且只是用一次,如果是某些比较核心的业务配置需要加密的话,jasypt是支持不了的,无法做到实时更新。下章节我会讲解如何修改Apollo的源码来解决这2个问题。
扩展Apollo支持存储加解密
前面章节中给大家介绍了如何使用jasypt为Apollo中的配置进行加解密操作,基本的需求是能够实现的,但还是有一些不足的地方。
jasypt只是在启动的时候将Spring中带有ENC(xx)这种格式的配置进行解密,当配置发生修改时无法更新。由于Apollo框架本身没有这种对配置加解密的功能,如果我们想实现加解密,并且能够动态的更新,就需要对Apollo的源码做一些修改来满足需求。
对源码修改还需要重新打包,笔者在这边介绍一个比较简单的实现方式,就是创建一个跟Apollo框架中一模一样的类名进行覆盖,这样也不用替换已经在使用的客户端。
如果配置中心存储的内容是加密的,意味着Apollo客户端从配置中心拉取下来的配置也是加密之后的,我们需要在配置拉取下来之后就对配置进行解密,然后再走后面的流程,比如绑定到Spring中。在这个业务点进行切入之后,配置中心加密的内容就可以自动变成解密后的明文,对使用者透明。
通过分析Apollo的源码,笔者找到了一个最合适的切入点来做这件事情,这个类就是com.ctrip.framework.apollo.internals.DefaultConfig,DefaultConfig是Coonfig接口的实现类,配置的初始化和获取都会经过DefaultConfig的处理。
在DefaultConfig内部有一个更新配置的方法updateConfig,可以在这个方法中对加密的数据进行解密处理:
private void updateConfig(Properties newConfigProperties, ConfigSourceType sourceType) {
Set<Object> keys = newConfigProperties.keySet();
for (Object k : keys) {
String key = k.toString();
String value = newConfigProperties.getProperty(key);
// 加密Value
if (value.startsWith("ENC(") && value.endsWith(")")) {
logger.debug("加密Value {}", value);
// 解密然后重新赋值
try {
String decryptValue = AesEncryptUtils.aesDecrypt(value.substring(3, value.length()-1), DECRYPT_KEY);
newConfigProperties.setProperty(key, decryptValue);
} catch (Exception e) {
logger.error("加密配置解密失败", e);
}
}
}
m_configProperties.set(newConfigProperties);
m_sourceType = sourceType;
}
这边使用了AES来解密,也就是说配置中心的加密内容也需要用相同的加密算法进行加密,至于格式的话还是用的ENC(xx)这种格式来标识这就是一个加密的配置内容。解密之后将解密的明文内容重新赋值到Properties 中,其他的流程不变。
创建一个加密测试类,加密配置内容,复制存储到Apollo中
public class Test {
public static void main(String[] args) {
String msg = "hello yinjihaun";
try {
String encryptMsg = AesEncryptUtils.aesEncrypt(msg, "1111222233334444");
System.out.println(encryptMsg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出内容如下:
Ke4LIPGOp3jCwbIHtmhmBA==
存储到Apollo中需要用ENC将加密内容包起来,如下:
test.input = ENC(Ke4LIPGOp3jCwbIHtmhmBA==)
还是用之前的代码进行测试,Config获取和Spring注入的方式如可以成功的获取到解密的数据,并且在配置中心修改后也能实时推送到客户端成功解密。
本文摘自于《Spring Cloud微服务 入门 实战与进阶》一书。

终于把Apollo存储加密这件事搞定了的更多相关文章
- iOS 3DES加密解密(一行代码搞定)
3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称.它相当于是对每个数据块应用三次DES加密算法.由于计 ...
- 对象存储 COS 帮您轻松搞定跨域访问需求
背景 早期为了避免 CSRF(跨站请求伪造) 攻击,浏览器引入了 "同源策略" 机制.如果两个 URL 的协议,主机名(域名/IP),端口号一致,则视为这两个 URL " ...
- 关于 Java 对象序列化您不知道的 5 件事
数年前,当和一个软件团队一起用 Java 语言编写一个应用程序时,我体会到比一般程序员多知道一点关于 Java 对象序列化的知识所带来的好处. 关于本系列 您觉得自己懂 Java 编程?事实上,大多数 ...
- (转)关于 Java 对象序列化您不知道的 5 件事
关于 Java 对象序列化您不知道的 5 件事 转自:http://developer.51cto.com/art/201506/479979.htm 数年前,当和一个软件团队一起用 Java 语言编 ...
- 从Oracle迁移到Mysql之前必须知道的50件事
1. 对子查询的优化表现不佳. 2. 对复杂查询的处理较弱 3. 查询优化器不够成熟 4. 性能优化工具与度量信息不足 5. 审计功能相对较弱 6. 安全功能不成熟,甚至可以说很粗糙.没有用户组与角色 ...
- 转载:关于 Token,你应该知道的十件事
关于 Token,你应该知道的十件事 原文地址:http://alvinzhu.me/blog/2014/08/26/10-things-you-should-know-about-tokens/ 原 ...
- 从Oracle转到Mysql前需了解的50件事
我本人比较关心的几点: 1. 对子查询的优化表现不佳. 2. 对复杂查询的处理较弱 4. 性能优化工具与度量信息不足 12. 支持 SMP (对称多处理器),但是如果每个处理器超过 4 或 8 个核( ...
- 安装完CentOS 7 后必做的七件事
CentOS是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后,首要的工作肯定是加强它的安全性,以下列出的七件事 ...
- 安装完CentOS 7 后必做的七件事[转]
CentOS是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后,首要的工作肯定是加强它的安全性,以下列出的七件事 ...
随机推荐
- 大疆无人机 Android 开发总结——视频解码
DJI_Mobile_SDK是大疆为开发者提供的开发无人机应用的开发接口,可以实现对无人机飞行的控制,也可以利用无人机相机完成一些视觉任务.目前网上的开发教程主要集中于DJI 开发者社区,网上的资源非 ...
- Navicat Premium 连接oracle 提示ORA-01017:用户名/口令无效;登陆被拒绝
Navicat Premium 连接oracle,密码明明是对的,还是提示 ORA-01017:用户名/口令无效:登陆被拒绝.而用Pl/SQL 连接没有问题. 其实用户名和密码是对的,但还是会报错,这 ...
- centos和Ubuntu系统最小化安装基础命令
CentOS系统常用的基础软件如下 yum install vim iotop bc gcc gcc-c++ glibc glibc-devel pcre \ pcre-devel openssl o ...
- 【机器学习基础】交叉熵(cross entropy)损失函数是凸函数吗?
之所以会有这个问题,是因为在学习 logistic regression 时,<统计机器学习>一书说它的负对数似然函数是凸函数,而 logistic regression 的负对数似然函数 ...
- cURL无法访问TLS网站故障解决
大多数人都厌烦使用老旧的系统,无论软件还是硬件.但有的时候又不得不困守其中,坚持延续着系统的寿命,或者还需要点几柱香,祈求神佛的护佑. Linux是一个模块化极好的操作系统,得益于此,当其中有组件落伍 ...
- ArrayList和LinkedList介绍
java.util.ArrayList集合的数据存储结构是数组,且是多线程,元素增删慢,查找快, 由于日常使用开发大多数为查询数据,遍历数据,所以ArrayList是最常用的集合.上一节已写了. ja ...
- JavaScript空字符串判断
JavaScript空字符串判断 本文完整示例代码GIT仓: 测试用例完整代码:isNullOrEmpty jPublic GIT仓:jPublic 比较常见写法 if (str == 'undefi ...
- RocketMQ(二):producer客户端实践
MQ解耦了生产者和消费者,前提是有一个稳定强大的消息服务,我们只管与之通信即可. 所以,和MqServer通信是什么样的?难否? 0. 发送端demo /** * This class demonst ...
- vscod如何自定义 python虚拟环境
参考文档:https://code.visualstudio.com/docs/python/environments 1.创建虚拟环境,cd到当前目录 py -3 -m venv env 2.Ctr ...
- Flask 教程 第七章:错误处理
本文翻译自The Flask Mega-Tutorial Part VII: Error Handling 这是Flask Mega-Tutorial系列的第七部分,我将告诉你如何在Flask应用中进 ...