写于:2019-01-12 00:00:37
code.7z
一、Spring Cloud Config 数据传输安全
针对数据传输的问题,在保证内网访问的前提下,相对而言,数据的安全还是相对比较可靠的。
不过为了让数据不以明文的方式进行传输,可以通过 Spring Cloud 支持的加密属性对配置进行加密操作。
二、两种加密方式案例
以上述案例代码进行拓展。
2.1、对称加密
模拟对 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
为明文显示。
2.2、非对称加密(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}
,否则不生效
三、扩展:配置文件加解密源码分析
3.1、Config Server 提供的 API
在 Spring Cloud Config 中提供了两套 API 接口,为 EnvironmentController
, EncryptionController
EnvironmentController
:为获取配置文件配置属性提供 APIEncryptionController
:提供单个字符的加解密功能
两套 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图
3.2、提供加解密功能的代码组
关键类 TextEncryptorLocator
和 TextEncryptor
。
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)
非对称加密的代码对象为 SingleTextEncryptorLocator
和 HexEncodingTextEncryptor
这里的
HexEncodingTextEncryptor
采用的委派的方式,将加解密操作委派给了AesBytesEncryptor
来实现
代码实现对象结构如下
非对称解密的流程,UML 流程图如下
具体操作可以看代码,代码很直观
非对称加密(RSA)
代码实现对象结构如下:
执行 RSA 加解密的流程包含了三个对象
LocatorTextEncryptor
,KeyStoreTextEncryptorLocator
和RsaSecretEncryptor
以 RSA 解密流程为主,UML 流程图如下:
具体操作可以看代码,代码很直观