本文摘自于《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存储加密这件事搞定了的更多相关文章

  1. iOS 3DES加密解密(一行代码搞定)

    3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称.它相当于是对每个数据块应用三次DES加密算法.由于计 ...

  2. 对象存储 COS 帮您轻松搞定跨域访问需求

    背景 早期为了避免 CSRF(跨站请求伪造) 攻击,浏览器引入了 "同源策略" 机制.如果两个 URL 的协议,主机名(域名/IP),端口号一致,则视为这两个 URL " ...

  3. 关于 Java 对象序列化您不知道的 5 件事

    数年前,当和一个软件团队一起用 Java 语言编写一个应用程序时,我体会到比一般程序员多知道一点关于 Java 对象序列化的知识所带来的好处. 关于本系列 您觉得自己懂 Java 编程?事实上,大多数 ...

  4. (转)关于 Java 对象序列化您不知道的 5 件事

    关于 Java 对象序列化您不知道的 5 件事 转自:http://developer.51cto.com/art/201506/479979.htm 数年前,当和一个软件团队一起用 Java 语言编 ...

  5. 从Oracle迁移到Mysql之前必须知道的50件事

    1. 对子查询的优化表现不佳. 2. 对复杂查询的处理较弱 3. 查询优化器不够成熟 4. 性能优化工具与度量信息不足 5. 审计功能相对较弱 6. 安全功能不成熟,甚至可以说很粗糙.没有用户组与角色 ...

  6. 转载:关于 Token,你应该知道的十件事

    关于 Token,你应该知道的十件事 原文地址:http://alvinzhu.me/blog/2014/08/26/10-things-you-should-know-about-tokens/ 原 ...

  7. 从Oracle转到Mysql前需了解的50件事

    我本人比较关心的几点: 1. 对子查询的优化表现不佳. 2. 对复杂查询的处理较弱 4. 性能优化工具与度量信息不足 12. 支持 SMP (对称多处理器),但是如果每个处理器超过 4 或 8 个核( ...

  8. 安装完CentOS 7 后必做的七件事

    CentOS是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后,首要的工作肯定是加强它的安全性,以下列出的七件事 ...

  9. 安装完CentOS 7 后必做的七件事[转]

    CentOS是最多人用来运行服务器的 Linux 版本,最新版本是 CentOS 7.当你兴趣勃勃地在一台主机或 VPS 上安装 CentOS 7 后,首要的工作肯定是加强它的安全性,以下列出的七件事 ...

随机推荐

  1. iOS中nil、 Nil、 NULL和NSNull的区别

    参考链接:https://www.jianshu.com/p/c3017ae6684a

  2. jsp + js + 前端弹出框

    在项目中,前端页面我们时常需要各种各样的弹出框: 1.alert对话框:显示含有给定消息的"JavaScript Alert"对话框 代码: var a = "Hello ...

  3. diango中让装了装饰器的函数的名字不是inner,而是原来的名字

    让装了装饰器的函数的名字不是inner,而是原来的名字 from functools import wraps def wrapper(func): @wraps(func) # 复制了原来函数的名字 ...

  4. Python生成requirements.txt方法

    在查看别人的Python项目时,经常会看到一个requirements.txt文件,里面记录了当前程序的所有依赖包及版本号,其作用是用来在另一个环境上重新构建项目所需要的运行环境依赖. require ...

  5. git版本对比

    1.git diff版本比对 (未添加到暂存区间之前的区别对比) 未进行修改,则显示为空 进行文件修改,再执行git diff 当执行git add .  之后,再次git diff则为空 缓存和提交 ...

  6. fiddler 常用命令及快捷键

    1.搜索: ?  baidu.com 2.bpu 发起请求时中断 设置断点 bpu baidu.com 取消断点:bpu 3.bpafter  收到响应后中断 设置断点:bpafter baidu.c ...

  7. 关于python的中国历年城市天气信息爬取

    一.主题式网络爬虫设计方案(15分)1.主题式网络爬虫名称 关于python的中国城市天气网爬取 2.主题式网络爬虫爬取的内容与数据特征分析 爬取中国天气网各个城市每年各个月份的天气数据, 包括最高城 ...

  8. [译]Vulkan教程(28)Image视图和采样器

    [译]Vulkan教程(28)Image视图和采样器 Image view and sampler - Image视图和采样器 In this chapter we're going to creat ...

  9. C++ 面向对象程序设计复习大纲

     这是我在准备C++考试时整理的提纲,如果是通过搜索引擎搜索到这篇博客的师弟师妹,建议还是先参照PPT和课本,这个大纲也不是很准确,自己总结会更有收获,多去理解含义,不要死记硬背,否则遇到概念辨析题会 ...

  10. JDK1.8源码分析02之阅读源码顺序

    序言:阅读JDK源码应该从何开始,有计划,有步骤的深入学习呢? 下面就分享一篇比较好的学习源码顺序的文章,给了我们再阅读源码时,一个指导性的标志,而不会迷失方向. 很多java开发的小伙伴都会阅读jd ...