写于:2019-01-12 00:00:37
code.7z

一、Spring Cloud Config 数据传输安全

针对数据传输的问题,在保证内网访问的前提下,相对而言,数据的安全还是相对比较可靠的。

不过为了让数据不以明文的方式进行传输,可以通过 Spring Cloud 支持的加密属性对配置进行加密操作。

二、两种加密方式案例

《Spring Cloud Config 中的案例》

以上述案例代码进行拓展。

项目结构如下:
02.png

2.1、对称加密

模拟对 clietn 端 spring.application.name 配置的加密

服务端操作

step1、服务端 config Server bootstrap.properties 中追加配置

  1. ## 对称加密秘钥 key
  2. encrypt.key = symmetrical_encryption_key

step2、启动服务端服务,在 postman 中进行操作

访问API http://localhost:28080/encrypt
01.png
对需要加密的内容进行加密操作

客户端操作

更改配置文件中 spring.application.name 参数为加密后的密文

如下:
03.png

需要在密文前加 {cipher},否则不生效

通过服务端验证密文被解密

访问 http://localhost:28080/config-cliet/default 结果如下:

  1. {
  2. "name": "config-client",
  3. "profiles": [
  4. "default"
  5. ],
  6. "label": null,
  7. "version": "261eed1c431d0f9365e994a8b57e840e91b1b65b",
  8. "state": null,
  9. "propertySources": [
  10. {
  11. "name": "xxxxxx/case-1/config-client/config-client.properties",
  12. "source": {
  13. "spring.cloud.config.profile": "default",
  14. "server.port": "9991",
  15. "spring.application.name": "config-clien-default"
  16. }
  17. }
  18. ]
  19. }

能够发现,此时的 spring.application.name 为明文显示。

2.2、非对称加密(RSA)

服务端操作

step1、生成 RSA 文件

  1. keytool -genkeypair -alias config-server-key -keyalg RSA -dname "CN=Config Server,OU=QGF,L=Beijing,S=Beijing,C=CN" -keypass zhixing -keystore config-server.jks -storepass ZHIXING

参数详解:

  1. -genkeypair 参数即产生一对public keyprivate key
  2. -keyalg 指定生成key的算法,这里使用默认的RSA
  3. -dname 指定common name,即CN,用以验证key的身份。其中各项皆为自定义参数,OU为单位名称,O为组织名称,L为城市,S为省份/州,C为国家
  4. -keypass key的密码
  5. -keystore keystore的文件名
  6. -storepass 访问keystore的密码

step2、生成 config-server.jks 放入 服务端项目 resources 中,同时 pom 追加如下配置:

pom 文件配置 (否则无法加载 jks 文件)

  1. <build>
  2. <resources>
  3. <resource>
  4. <directory>src/main/resources</directory>
  5. <filtering>true</filtering>
  6. <excludes>
  7. <exclude>**/*.jks</exclude>
  8. </excludes>
  9. </resource>
  10. <resource>
  11. <directory>src/main/resources</directory>
  12. <filtering>false</filtering>
  13. <includes>
  14. <include>**/*.jks</include>
  15. </includes>
  16. </resource>
  17. </resources>
  18. </build>

step3、配置 bootstrap.properties (记得注释掉 对称加密的配置(对称和非对称不能同时存在))

  1. ## 放置在 resources 中的文件名
  2. encrypt.key-store.location = config-server.jks
  3. ## 对应 keystore
  4. encrypt.key-store.alias = config-server-key
  5. ## 对应 -storepass
  6. encrypt.key-store.password = ZHIXING
  7. ## 对应 -keypass
  8. encrypt.key-store.secret = zhixing

step4、启动服务端服务,在 postman 中进行操作

访问API http://localhost:28080/encrypt
04.png
对需要加密的内容进行加密操作

客户端操作

更改配置文件中 spring.application.name 参数为加密后的密文

如下:
05.png

需要在密文前加 {cipher},否则不生效

三、扩展:配置文件加解密源码分析

3.1、Config Server 提供的 API

在 Spring Cloud Config 中提供了两套 API 接口,为 EnvironmentController, EncryptionController

  • EnvironmentController:为获取配置文件配置属性提供 API
  • EncryptionController:提供单个字符的加解密功能
    两套 API 的操作是相关联的,将需要加密的字符通过 EncryptionController 进行加密,客户端通过 EnvironmentController 拉取的配置的时候,如果有需要解密的配置,加通过相同的算法进行解密

EncryptionController 部分代码

  1. @RestController
  2. @RequestMapping(path = "${spring.cloud.config.server.prefix:}")
  3. public class EncryptionController {
  4. volatile private TextEncryptorLocator encryptor;
  5. @RequestMapping(value = "/encrypt/{name}/{profiles}", method = RequestMethod.POST)
  6. public String encrypt(@PathVariable String name, @PathVariable String profiles,
  7. @RequestBody String data, @RequestHeader("Content-Type") MediaType type) {
  8. ......
  9. String encrypted = this.helper.addPrefix(keys,
  10. this.encryptor.locate(keys).encrypt(textToEncrypt));
  11. logger.info("Encrypted data");
  12. return encrypted;
  13. }
  14. @RequestMapping(value = "/decrypt/{name}/{profiles}", method = RequestMethod.POST)
  15. public String decrypt(@PathVariable String name, @PathVariable String profiles,
  16. @RequestBody String data, @RequestHeader("Content-Type") MediaType type) {
  17. ......
  18. TextEncryptor encryptor = this.encryptor.locate(encryptorKeys);
  19. return decrypted;
  20. }
  21. }

上述代码主要两个 API

  • encrypt 字符加密API
  • decrypt 字符解密API

而加解密的操作均由 TextEncryptorLocator 来实现

EnvironmentController 部分代码

  1. */
  2. @RestController
  3. @RequestMapping(method = RequestMethod.GET, path = "${spring.cloud.config.server.prefix:}")
  4. public class EnvironmentController {
  5. private EnvironmentRepository repository;
  6. @RequestMapping("/{name}/{profiles}/{label:.*}")
  7. public Environment labelled(@PathVariable String name, @PathVariable String profiles,
  8. @PathVariable String label) {
  9. ......
  10. Environment environment = this.repository.findOne(name, profiles, label);
  11. ......
  12. return environment;
  13. }
  14. }

通过 EnvironmentController 的配置类能够知道该类中的 EnvironmentRepository 参数实现类为 EnvironmentEncryptorEnvironmentRepository

关键对象关系如下:
09.png
获取 Environment 环境对象 UML图
10.png

3.2、提供加解密功能的代码组

关键类 TextEncryptorLocatorTextEncryptor

TextEncryptorLocator : 加解密定位器,持有 TextEncryptor 实例对象

TextEncryptor: 真正进行加解密操作的类。

相关代码如下:
06.png
两者的关联:通过 TextEncryptorLocator#locate 定位到指定的 TextEncryptor,然后由 TextEncryptor 进行加解密操作。

通过自动配置查找相关的配置信息

在 Spring Cloud Config 中 TextEncryptor 的配置类 DefaultTextEncryptorConfiguration 代码如下:

  1. @ConditionalOnMissingBean(TextEncryptor.class)
  2. @Configuration
  3. class DefaultTextEncryptorConfiguration {
  4. @Autowired
  5. private KeyProperties key;
  6. @Autowired(required = false)
  7. private TextEncryptorLocator locator;
  8. @Bean
  9. public TextEncryptor defaultTextEncryptor() {
  10. // 如果存在 RSA 非对称加密,
  11. if (this.locator != null) {
  12. return new LocatorTextEncryptor(this.locator);
  13. }
  14. // 如果 encrypt.key 配置存在,则为 AES 对称加密
  15. if (StringUtils.hasText(this.key.getKey())) {
  16. return new EncryptorFactory(this.key.getSalt()).create(this.key.getKey());
  17. }
  18. // 都不存在,则为空实现,也就是没有加解密功能的空实现
  19. return Encryptors.noOpText();
  20. }
  21. }

上述配置描述了加解密算法的相关配置顺序:

  • 非对称加密-RSA 优先
  • 对称加密-AES 其次
  • 在没有的话,就是一个空实现 NoOpTextEncryptor

对称加密(AES)

非对称加密的代码对象为 SingleTextEncryptorLocatorHexEncodingTextEncryptor

这里的 HexEncodingTextEncryptor 采用的委派的方式,将加解密操作委派给了 AesBytesEncryptor 来实现

代码实现对象结构如下
11.png
非对称解密的流程,UML 流程图如下
12.png

具体操作可以看代码,代码很直观

非对称加密(RSA)

代码实现对象结构如下:
07.png

执行 RSA 加解密的流程包含了三个对象 LocatorTextEncryptorKeyStoreTextEncryptorLocatorRsaSecretEncryptor

以 RSA 解密流程为主,UML 流程图如下:
08.png

具体操作可以看代码,代码很直观