写于:2019-01-12 00:00:37

# Spring Cloud Config 数据传输安全

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

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

# 两种加密方式案例

Spring Cloud Config 中的案例

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

项目结构如下:

# 对称加密

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

# 服务端操作

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

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

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

访问API http://localhost:28080/encrypt

对需要加密的内容进行加密操作

# 客户端操作

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

如下:

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

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

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

{
    "name": "config-client",
    "profiles": [
        "default"
    ],
    "label": null,
    "version": "261eed1c431d0f9365e994a8b57e840e91b1b65b",
    "state": null,
    "propertySources": [
        {
            "name": "xxxxxx/case-1/config-client/config-client.properties",
            "source": {
                "spring.cloud.config.profile": "default",
                "server.port": "9991",
                "spring.application.name": "config-clien-default"  
            }
        }
    ]
}

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

# 非对称加密(RSA)

# 服务端操作

step1、生成 RSA 文件

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

参数详解:

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

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

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

<build>
      <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>**/*.jks</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.jks</include>
                </includes>
            </resource>
        </resources>
</build>

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

## 放置在 resources 中的文件名
encrypt.key-store.location  = config-server.jks 
## 对应 keystore
encrypt.key-store.alias     = config-server-key
## 对应  -storepass
encrypt.key-store.password  = ZHIXING
## 对应 -keypass
encrypt.key-store.secret    = zhixing

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

访问API http://localhost:28080/encrypt

对需要加密的内容进行加密操作

# 客户端操作

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

如下:

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

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

# Config Server 提供的 API

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

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

# EncryptionController 部分代码

@RestController
@RequestMapping(path = "${spring.cloud.config.server.prefix:}")
public class EncryptionController {
  volatile private TextEncryptorLocator encryptor;
  @RequestMapping(value = "/encrypt/{name}/{profiles}", method = RequestMethod.POST)
  public String encrypt(@PathVariable String name, @PathVariable String profiles,
      @RequestBody String data, @RequestHeader("Content-Type") MediaType type) {
    ......
    String encrypted = this.helper.addPrefix(keys,
        this.encryptor.locate(keys).encrypt(textToEncrypt));
    logger.info("Encrypted data");
    return encrypted;
  }

  @RequestMapping(value = "/decrypt/{name}/{profiles}", method = RequestMethod.POST)
  public String decrypt(@PathVariable String name, @PathVariable String profiles,
      @RequestBody String data, @RequestHeader("Content-Type") MediaType type) {
      ......
      TextEncryptor encryptor = this.encryptor.locate(encryptorKeys);
      return decrypted;
  }
}

上述代码主要两个 API

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

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

# EnvironmentController 部分代码

 */
@RestController
@RequestMapping(method = RequestMethod.GET, path = "${spring.cloud.config.server.prefix:}")
public class EnvironmentController {
  private EnvironmentRepository repository;
  @RequestMapping("/{name}/{profiles}/{label:.*}")
  public Environment labelled(@PathVariable String name, @PathVariable String profiles,
      @PathVariable String label) {
    ......
    Environment environment = this.repository.findOne(name, profiles, label);
    ......
    return environment;
  }
}

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

关键对象关系如下:

获取 Environment 环境对象 UML图

# 提供加解密功能的代码组

关键类 TextEncryptorLocatorTextEncryptor

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

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

相关代码如下:

两者的关联:通过 TextEncryptorLocator#locate 定位到指定的 TextEncryptor,然后由 TextEncryptor 进行加解密操作。

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

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

@ConditionalOnMissingBean(TextEncryptor.class)
@Configuration
class DefaultTextEncryptorConfiguration {

  @Autowired
  private KeyProperties key;

  @Autowired(required = false)
  private TextEncryptorLocator locator;

  @Bean
  public TextEncryptor defaultTextEncryptor() {
    // 如果存在 RSA 非对称加密,
    if (this.locator != null) {
      return new LocatorTextEncryptor(this.locator);
    }
    // 如果 encrypt.key 配置存在,则为 AES 对称加密
    if (StringUtils.hasText(this.key.getKey())) {
      return new EncryptorFactory(this.key.getSalt()).create(this.key.getKey());
    }
    // 都不存在,则为空实现,也就是没有加解密功能的空实现
    return Encryptors.noOpText();
  }

}

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

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

# 对称加密(AES)

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

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

代码实现对象结构如下

非对称解密的流程,UML 流程图如下

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

# 非对称加密(RSA)

代码实现对象结构如下:

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

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

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

精彩内容推送,请关注公众号!
最近更新时间: 4/10/2020, 10:09:47 PM