跳到主要内容
版本:5.0.1

加密与解密

DeepSeek V3 中英对照 Encryption and Decryption

important

要使用加密和解密功能,您需要在 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>)。这主要是为了防止密文被用作密码并意外泄露。

如果你为配置客户端应用程序设置了远程配置仓库,它可能包含一个类似于以下的 application.yml 文件:

spring:
datasource:
username: dbuser
password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'

application.properties 文件中的加密值不能包裹在引号中,否则该值将无法解密。以下示例展示了可行的值:

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

你可以安全地将这段纯文本推送到共享的 git 仓库中,而秘密密码仍然受到保护。

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

$ 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

将加密后的值添加 {cipher} 前缀,然后放入 YAML 或 properties 文件中,再提交并推送到远程(可能不安全的)存储库。

/encrypt 和 /decrypt 端点还接受 /*/{application}/{profiles} 形式的路径,当客户端调用主环境资源时,可用于按应用程序(名称)和配置文件控制加密。

备注

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

安装了 Spring Cloud CLI 扩展的 spring 命令行客户端也可用于加密和解密,如下例所示:

$ 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 前缀。

如果配置客户端收到此响应,然后将其添加到应用程序的 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
}

在这种情况下,如果配置客户端收到上述响应,并从Environment中请求SharedPassword的值,它将获得加密后的值,而不是Fill_me_in。