专栏首页玩转JavaEE终于把Apollo存储加密这件事搞定了 | 本月第二次无套路送书!

终于把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微服务 入门 实战与进阶》一书。这是朋友写的一本新书,豆瓣评分 8.2 。

接下来,松哥要送书了!

先给大家大概介绍一下这本书:

这是一部从技术原理、工程实践和进阶提升3个维度讲解SpringCloud微服务架构与开发的著作。

作者是资深微服务技术专家,本书从技术原理、工程实践、进阶提升3个维度详解Spring Cloud微服务架构与开发,快速实现从入门到进阶,掌握微服务典型场景解决方案。

全书共21章,分为4个部分:

第一部分准备篇(第1~2章)

首先对微服务和Spring Cloud的概念、优劣势、功能模块等做了整体性的介绍,然后演示了如何搭建Spring Cloud的开发环境,最后对Spring Boot做了详细的介绍。

第二部分基础篇(第3~7章)

对Eureka 注册中心、客户端负载均衡 Ribbon、声明式 REST 客户端 Feign、Hystrix 服务容错处理、API 网关等Spring Cloud的重要模块的技术原理、配置、使用等做了详尽的讲解。

第三部分实战篇(第8~14章)

对微服务架构中的普遍问题给出了实战解决方案,包括选择配置中心Apollo、自研发配置中心、分布式跟踪、微服务安全认证、SpringBoot Admin 管理微服务、快速生成API 文档等实用性内容。

第四部分高级篇(15-21)

重点讲解了Spring Cloud的扩展性的使用,比如API网关Zuul, Spring Cloud Gateway、微服务的缓存和存储、分布式事务解决方案、任务调度、分库分表,以及大量优秀的生产实践经验等。

本书基于比较稳定的 Spring Cloud Finchley.SR2 版本和 Spring Boot 2.0.6.RELEASE 版本编写。

同时将示列代码进行标准的归档,方便读者参考和运行。

为了感谢各位小伙伴一直以来的支持,松哥在这里送出 5 本 《Spring Cloud微服务入门实战与进阶》,书要怎么送呢?

大伙留言说一说你印象最深刻的一次面试,我会从中挑选出 5 名幸运读者,《Spring Cloud微服务入门实战与进阶》包邮到家!(平时分享转发、点赞较多的小伙伴获奖概率更大哦~

本文分享自微信公众号 - 牧码小子(a_javaboy)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SpringMVC 方法三种类型返回值总结,你用过几种?

    SpringMVC 现在算是 Java 领域的一个基础性框架了,很多人天天用,可是对于 SpringMVC 方法的返回值,你又是否完全清楚呢?今天松哥就来和大家...

    江南一点雨
  • MongoDB文档查询操作(一)

    上篇文章我们主要介绍了MongoDB的修改操作,本文我们来看看查询操作。 本文是MongoDB系列的第五篇文章,了解前面的文章有助于更好的理解本文: ---- ...

    江南一点雨
  • MongoDB文档查询操作(二)

    上篇文章我们对MongoDB中的查询操作做了简单介绍,本文我们继续来看更丰富的查询操作。 本文是MongoDB系列的第六篇文章,了解前面的文章有助于更好的理解本...

    江南一点雨
  • 终于把Apollo存储加密这件事搞定了

    一些比较重要的配置信息,比如密码之类的敏感配置,我们希望将配置加密存储,保证安全性。Apollo框架本身没有提供数据加密的功能,如果想要实现数据加密的功能有两种...

    猿天地
  • ICML入选论文名单公布,我国主力选手是清华和腾讯

    今年的竞争更加激烈,2473篇的投稿总数又突破了纪录,比去年同样成为历史新高的1676篇,多出几乎一半。

    量子位
  • Visual studio 创建项目失败vstemplate

    Visual studio 创建项目失败 提示 the vstemplate file references the wizard class ‘Microso...

    林德熙
  • 洛桑理工27页PPT带你入坑GCN:Graph上的深度学习报告(附下载)

    导读:洛桑理工学院教授Michaël Defferrard在Deep Learning on Graphs at the Graph Signal Proces...

    华章科技
  • 大白话说Java泛型:入门、使用、原理

    远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的。当时 Java 程序员们写集合类的代码都是类似于下面这样: List list = new Arr...

    陈树义
  • 实验8 OpenGL交互

    (1) 运行示范实验代码1,掌握程序鼠标交互方法,尝试为其添加键盘与菜单控制,实现同样功能;

    步行者08
  • miaosha

    在 Redis 里,所谓 SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果

    大学里的混子

扫码关注云+社区

领取腾讯云代金券