加密和解密
要使用加密和解密功能,您需要在 JVM 中安装完全强度的 JCE(默认不包含)。您可以从 Oracle 下载“Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files”,然后按照安装说明进行操作(本质上,您需要将 JRE lib/security 目录中的两个策略文件替换为您下载的文件)。 |
如果远程属性源包含加密内容(以 {cipher}
开头的值),它们会在通过 HTTP 发送到客户端之前被解密。这种设置的主要优点是属性值在“静止状态”(例如,在 Git 仓库中)时不需要是纯文本。如果某个值无法解密,它将从属性源中移除,并添加一个带有相同键但以 invalid
为前缀的附加属性,其值表示“不适用”(通常是 <n/a>
)。这主要是为了防止密文被用作密码并意外泄露。
如果您为 Config Client 应用程序设置了远程配置仓库,它可能包含一个类似于以下内容的 application.yml
文件
spring:
datasource:
username: dbuser
password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'
application.properties
文件中的加密值不能用引号括起来。否则,该值将不会被解密。以下示例显示了有效的配置值
spring.datasource.username: dbuser spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
您可以安全地将此纯文本推送到共享 Git 仓库,并且秘密密码仍受到保护。
服务器还暴露了 /encrypt
和 /decrypt
端点(假设这些端点是安全的,并且只能由授权代理访问)。如果您编辑远程配置文件,可以使用 Config Server 通过向 /encrypt
端点发送 POST 请求来加密值,如以下示例所示
$ curl localhost:8888/encrypt -s -d mysecret 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
如果您使用 curl 进行测试,请使用 --data-urlencode (而不是 -d ),并在要加密的值前面加上 = (curl 需要这样做),或者设置明确的 Content-Type: text/plain 以确保当存在特殊字符('+' 特别棘手)时 curl 能正确编码数据。 |
请确保不要将任何 curl 命令统计信息包含在加密值中,这就是为什么示例使用 -s 选项来静默输出。将值输出到文件有助于避免此问题。 |
逆向操作也通过 /decrypt
端点可用(前提是服务器配置了对称密钥或完整的密钥对),如以下示例所示
$ curl localhost:8888/decrypt -s -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda mysecret
获取加密后的值,并在将其放入 YAML 或 properties 文件中,以及提交并推送到远程(可能不安全)存储之前,加上 {cipher}
前缀。
/encrypt
和 /decrypt
端点也都接受 /*/{application}/{profiles}
形式的路径,当客户端调用主环境资源时,这可用于按应用程序(名称)和按配置文件控制加密。
要以这种细粒度的方式控制加密,您还必须提供一个 @Bean ,类型为 TextEncryptorLocator ,它为每个名称和配置文件创建一个不同的加密器。默认提供的加密器不是这样做的(所有加密都使用相同的密钥)。 |
Spring 命令行客户端(安装了 Spring Cloud CLI 扩展)也可用于加密和解密,如以下示例所示
$ spring encrypt mysecret --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda $ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda mysecret
要在文件中使用密钥(例如用于加密的 RSA 公钥),在密钥值前加上 "@" 并提供文件路径,如以下示例所示
$ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
--key 参数是强制性的(尽管带有 -- 前缀)。 |
解密错误
当配置服务器无法解密某个值时,它将在 HTTP 响应中创建一个带有 invalid
前缀的属性。
例如
{
"label": null,
"name": "application",
"profiles": [
"prd"
],
"propertySources": [
{
"name": "file:/demo/configserver/application-prd.yaml",
"source": {
"invalid.SharedPassword": "<n/a>"
}
},
{
"name": "file:/demo/configserver/application.yaml",
"source": {
"SharedPassword": "Fill_me_in"
}
}
],
"state": null,
"version": null
}
在上面的示例中,配置服务器无法解密 application-prd.yaml
中 SharedPassword
的值,因此配置服务器在属性名称前加上了 invalid
前缀。
如果 Config Client 接收到此响应并将其添加到应用程序的 Environment
中,并且客户端请求 SharedPassword
的值,它将获得 Fill_me_in
。
如果您不希望配置服务器对无法解密的属性加上 invalid
前缀,则可以将 spring.cloud.config.server.encrypt.prefix-invalid-properties
设置为 false
。如果您这样做,来自配置服务器的相同响应将如下所示
"label": null,
"name": "application",
"profiles": [
"prd"
],
"propertySources": [
{
"name": "file:/demo/configserver/application-prd.yaml",
"source": {
"SharedPassword": "AYBKlpcZpaR36OcRDQjNIQl6fmnddAQhetMw/uyTpnn5fDj+unJ9QOEbqiPc9fX0N+CC8i+EJiN6nlH9Xqu6sH1tX/P6zg1CIy+ct/1RWGNbmQ256jc6vQaXhiN8sA8Mr6QiqYnMoBd+Jni/Miir5G3a7G9MmjbEUASKJOhUlIFKqL1IqB81RBT/cv0bg9kAiy5VBF1WppxP/PwtjECzbeUi2Y1jbpYb98rnc/qmRO3ZJam9fDNcPpW09qGFhGgJIujca257F7G4guS2w/7haVzNoyRiwHzZ14oL8AIxHLMBSJJF19ULlsMAkROj9o9TnwhL9r4rX9sAWk28c5eq77+iVpmlT3yoRdZqvMqffzKiibDlzz95Gmms7V7mctxrhNVOOWTwMSJvk94Y9ZPenljKgPJIV3Z1cqqx+W8JxFFeelOuYvMEe4bOVBh1TepGzzdWVdYbylgXJy35uRTZ2drybUe5+jc0hiAuujHz0zdY1FwOHfwzSsSidlYn4syPeuytnxTzn7fbWXeXetTTtDlmLRf8MBSzXzDFWNH0cNGOCQ=="
}
},
{
"name": "file:/demo/configserver/application.yaml",
"source": {
"SharedPassword": "Fill_me_in"
}
}
],
"state": null,
"version": null
}
在这种情况下,如果 Config Client 接收到上述响应并从 Environment
请求 SharedPassword
的值,它将获得加密后的值,而不是 Fill_me_in
。