此版本仍在开发中,目前尚不稳定。如需最新稳定版本,请使用 Spring Cloud Config 5.0.1spring-doc.cadn.net.cn

加密与解密

要使用加密和解密功能,您需要在JVM中安装完整强度的JCE(默认情况下不包含)。您可从Oracle下载“Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files”,并按照安装说明操作(本质上,您需要将下载的两个策略文件替换JRE目录下的lib/security子目录中的对应文件)。此操作仅适用于旧版Java。从Java 9开始,以及Java 8u161及以后版本起,默认已启用无限强度加密。

如果远程属性源包含加密内容(以 {cipher} 开头的值),则在通过 HTTP 发送给客户端之前会先进行解密。这种配置的主要优势在于,当属性值“处于静止状态”(例如,在 Git 仓库中)时,它们无需以明文形式存在。如果某个值无法解密,则该值将从属性源中移除,并添加一个具有相同键但前缀为 invalid 的额外属性,其值通常为 <n/a>(表示“不适用”)。此举主要是为了防止密文被误用作密码,从而意外泄露。spring-doc.cadn.net.cn

如果您为配置客户端应用程序设置了远程配置仓库,它可能包含一个类似于以下内容的 application.ymlspring-doc.cadn.net.cn

application.yml
spring:
  datasource:
    username: dbuser
    password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'

文件 application.properties 中的加密值不得用引号包裹。否则,该值将无法解密。以下示例展示了可正常工作的值:spring-doc.cadn.net.cn

application.properties
spring.datasource.username: dbuser
spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ

您可以安全地将此纯文本推送到共享的 git 仓库,而密钥密码仍会受到保护。spring-doc.cadn.net.cn

服务器还暴露了 /encrypt/decrypt 端点(假设这些端点已受保护,仅由授权代理访问)。
如果您编辑了一个远程配置文件,可以使用配置服务器通过向 /encrypt 端点发送 POST 请求来加密值,如下例所示:spring-doc.cadn.net.cn

$ curl localhost:8888/encrypt -s -d mysecret
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
如果您使用 curl 进行测试,请使用 --data-urlencode(而非 -d),并在要加密的值前加上 =(curl 要求这样做),或显式设置 Content-Type: text/plain,以确保在存在特殊字符(尤其是 '+')时 curl 能正确编码数据。
请确保在加密值中不包含任何 curl 命令的统计信息,这就是为什么示例中使用 -s 选项来静音它们的原因。将值输出到文件有助于避免此问题。

反向操作也可通过 /decrypt(前提是服务器已配置对称密钥或完整密钥对)实现,如下例所示:spring-doc.cadn.net.cn

$ curl localhost:8888/decrypt -s -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret

获取加密后的值,并在将其放入 YAML 或属性文件之前、以及在提交并推送到远程(可能不安全)存储之前,添加 {cipher} 前缀。spring-doc.cadn.net.cn

端点 /encrypt/decrypt 同样接受以 /*/{application}/{profiles} 形式指定的路径,当客户端调用主环境资源时,可用于按应用程序(名称)和配置文件级别分别控制加密。spring-doc.cadn.net.cn

为了以这种细粒度的方式控制加密,您还必须提供一个类型为 TextEncryptorLocator@Bean,该类型可为每个名称和配置文件创建不同的加密器。默认提供的那个则不会这样做(所有加密均使用相同的密钥)。

命令行客户端(已安装 Spring Cloud CLI 扩展)也可以用于加密和解密,如下例所示:spring-doc.cadn.net.cn

$ spring encrypt mysecret --key foo
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
$ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret

要使用文件中的密钥(例如用于加密的 RSA 公共密钥),请在密钥值前加上“@”符号,并提供文件路径,如下例所示:spring-doc.cadn.net.cn

$ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub
AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
参数 --key 是必需的(尽管带有 -- 前缀)。

解密错误

当配置服务器无法解密某个值时,它将在 HTTP 响应中创建一个 invalid 属性。spring-doc.cadn.net.cn

{
    "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
}

在上面的例子中,配置服务器无法解密 SharedPasswordapplication-prd.yaml 中的值,因此配置服务器将属性名前缀加上了 invalidspring-doc.cadn.net.cn

如果此响应由配置客户端接收,然后添加到应用程序的 Environment 中,并且客户端请求 SharedPassword 的值,则会得到 Fill_me_inspring-doc.cadn.net.cn

如果您不希望配置服务器为无法解密的属性添加前缀,可以将 invalid 设置为 spring.cloud.config.server.encrypt.prefix-invalid-properties,然后设置 false。如果执行此操作,则来自配置服务器的相同响应将如下所示:spring-doc.cadn.net.cn

    "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
}

在这种情况下,如果配置客户端接收到上述响应,并请求从 Environment 中获取值 SharedPassword,它将返回加密后的值,而不是 Fill_me_inspring-doc.cadn.net.cn