跳到主要内容

认证方法

DeepSeek V3 中英对照 Authentication Methods

不同的组织对安全性和身份验证有不同的需求。Vault 通过提供多种身份验证方法来反映这一需求。Spring Vault 支持多种身份验证机制。

外部化登录凭据

首次访问受保护系统的过程被称为安全引入(secure introduction)。任何客户端都需要临时的或永久的凭证来访问 Vault。将凭证外部化是一种保持代码可维护性的良好模式,但这也带来了增加泄露风险的问题。

将登录凭证披露给任何一方,都允许其登录 Vault 并访问底层角色所允许的机密信息。选择合适的客户端认证方式并将凭证注入到应用程序中,需要进行风险评估。

Spring 的 PropertySource 抽象 非常适合将配置保留在应用程序代码之外。你可以使用系统属性、环境变量或属性文件来存储登录凭据。每种方法都有其自身的属性。请记住,命令行和环境属性可以通过适当的操作系统访问级别进行内省。

示例 1. 将 vault.token 外化到 properties 文件中

@PropertySource("configuration.properties")
@Configuration
public class Config extends AbstractVaultConfiguration {

@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication(getEnvironment().getProperty("vault.token"));
}
}
java
备注

Spring 允许多种方式获取 Environment。在使用 VaultPropertySource 时,通过 @Autowired Environment environment 注入将无法提供 Environment,因为环境 bean 仍在构建中,而自动装配是在稍后的阶段进行的。你的配置类应该实现 ApplicationContextAware 并从 ApplicationContext 中获取 Environment

请参见 SecurePropertyUsage.java 以获取在组件和其他属性源中引用属性的示例。

Token 认证

Tokens 是 Vault 中身份验证的核心方法。Token 身份验证需要提供一个静态的 token。

备注

Token 认证是默认的认证方法。如果 Token 被泄露给未经授权的第三方,该第三方将能够访问 Vault 并获取目标客户端的机密信息。

通常情况下,Token 认证用于在外部创建和更新令牌的场景(例如 HashiCorp Vault 服务代理)。根据实际设置,您可能希望或不希望进行令牌的更新和撤销。有关 TTL 和令牌撤销的详细信息,请参阅 LifecycleAwareSessionManager

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("…");
}

// …
}
java

另请参阅:

AppId 认证

备注

Vault 已弃用 AppId 身份验证。请改用 AppRole 身份验证

Vault 支持 AppId 认证,该认证由两个难以猜测的令牌组成。AppId 默认为静态配置的 spring.application.name。第二个令牌是 UserId,它是由应用程序确定的一部分,通常与运行时环境相关。IP 地址、Mac 地址或 Docker 容器名称都是很好的例子。Spring Vault 支持 IP 地址、Mac 地址和静态 UserId(例如通过系统属性提供)。IP 和 Mac 地址以十六进制编码的 SHA256 哈希表示。

基于 IP 地址的 UserId 使用本地主机的 IP 地址。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder()
.appId("myapp")
.userIdMechanism(new IpAddressUserId())
.build();

return new AppIdAuthentication(options, restOperations());
}

// …
}
java

生成 IP 地址 UserId 的对应命令行命令是:

$ echo -n 192.168.99.1 | sha256sum
备注

包含 echo 的换行符会导致不同的哈希值,因此请确保包含 -n 标志。

基于 Mac 地址的 UserId 从绑定到本地主机的网络设备中获取。配置还允许指定一个 network-interface 提示来选择正确的设备。network-interface 的值是可选的,可以是接口名称或接口索引(从 0 开始)。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder()
.appId("myapp")
.userIdMechanism(new MacAddressUserId())
.build();

return new AppIdAuthentication(options, restOperations());
}

// …
}
java

生成 Mac 地址 UserId 的对应命令行命令是:

$ echo -n 0AFEDE1234AC | sha256sum
备注

Mac 地址需要大写且不带冒号。包含 echo 的换行符会导致不同的哈希值,因此请确保包含 -n 标志。

自定义用户 ID

一种更高级的方法允许你实现自己的 AppIdUserIdMechanism。这个类必须位于你的类路径中,并且必须实现 org.springframework.vault.authentication.AppIdUserIdMechanism 接口以及 createUserId 方法。每次使用 AppId 进行身份验证以获取令牌时,Spring Vault 都会通过调用 createUserId 来获取 UserId。

public class MyUserIdMechanism implements AppIdUserIdMechanism {

@Override
public String createUserId() {

String userId =
return userId;
}
}
java

AppRole 认证

AppRole 允许进行机器身份验证,类似于已被弃用的(自 Vault 0.6.1 起)AppId 身份验证。AppRole 身份验证由两个难以猜测的(秘密)令牌组成:RoleId 和 SecretId。

Spring Vault 支持通过提供仅 RoleId 或与提供的 SecretId 一起进行 AppRole 认证,并且可以从 Vault 获取 RoleId/SecretId(使用响应解包的推送和拉取模式)。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.roleId(RoleId.provided("…"))
.secretId(SecretId.wrapped(VaultToken.of("…")))
.build();

return new AppRoleAuthentication(options, restOperations());
}

// …
}
java

Spring Vault 也支持全拉模式:如果未提供 RoleId 和 SecretId,Spring Vault 将使用角色名称和初始令牌来检索它们。初始令牌可能与 TTL 和使用限制相关联。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

VaultToken initialToken = VaultToken.of("…");
AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
.appRole("…")
.roleId(RoleId.pull(initialToken))
.secretId(SecretId.pull(initialToken))
.build();

return new AppRoleAuthentication(options, restOperations());
}

// …
}
java

AWS-EC2 认证

aws-ec2 认证后端为 AWS EC2 实例提供了一种安全的引入机制,允许自动获取 Vault 令牌。与大多数 Vault 认证后端不同,该后端不需要首次部署或配置涉及安全敏感的凭证(如令牌、用户名/密码、客户端证书等)。相反,它将 AWS 视为可信第三方,并使用加密签名的动态元数据信息,这些信息唯一地代表每个 EC2 实例。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {
return new AwsEc2Authentication(restOperations());
}

// …
}
java

AWS-EC2 认证默认启用 nonce,以遵循首次使用信任(TOFU)原则。任何获得 PKCS#7 身份元数据访问权限的未经授权方都可以对 Vault 进行认证。

在首次登录时,Spring Vault 会生成一个随机数(nonce),该随机数与实例 ID 一起存储在认证后端中。重新认证时需要发送相同的随机数。任何其他方都无法获取该随机数,并且可以在 Vault 中触发警报以进行进一步的调查。

nonce 保存在内存中,在应用程序重启时会丢失。

AWS-EC2 认证角色是可选的,默认使用 AMI。你可以通过在 AwsEc2AuthenticationOptions 中设置来配置认证角色。

AWS-IAM 认证

aws 认证后端允许使用现有的 AWS IAM 凭证进行 Vault 登录。

AWS IAM 身份验证会创建一个签名的 HTTP 请求,该请求由 Vault 执行,以使用 AWS STS 的 GetCallerIdentity 方法获取签名者的身份。AWSv4 签名需要 IAM 凭据。

IAM 凭证可以从运行时环境中获取,也可以从外部提供。运行时环境(如 AWS-EC2、Lambda 和 ECS)如果分配了 IAM 主体,则不需要针对客户端进行凭证的特定配置,而是可以从其元数据源中获取这些凭证。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder()
.credentials(new BasicAWSCredentials()).build();

return new AwsIamAuthentication(options, restOperations());
}

// …
}
java

示例 2. 使用 AWS-EC2 实例配置文件作为凭证源

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder()
.credentialsProvider(InstanceProfileCredentialsProvider.getInstance()).build();

return new AwsIamAuthentication(options, restOperations());
}

// …
}
java

AwsIamAuthentication 需要 AWS Java SDK 依赖项(com.amazonaws:aws-java-sdk-core),因为认证实现使用了 AWS SDK 类型来处理凭证和请求签名。

你可以通过 AwsIamAuthenticationOptions 配置身份验证。

另请参阅:

Azure (MSI) 认证

azure 认证后端为 Azure VM 实例提供了一种安全的引入机制,允许自动获取 Vault 令牌。与大多数 Vault 认证后端不同,该后端不需要首次部署或配置安全敏感的凭据(令牌、用户名/密码、客户端证书等)。相反,它将 Azure 视为可信第三方,并使用可以绑定到 VM 实例的托管服务身份和实例元数据信息。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

AzureMsiAuthenticationOptions options = AzureMsiAuthenticationOptions.builder()
.role().build();

return new AzureMsiAuthentication(options, restOperations());
}

// …
}
java

Azure 身份验证需要有关 VM 环境的详细信息(订阅 ID、资源组名称、VM 名称)。这些详细信息可以通过 AzureMsiAuthenticationOptionsBuilder 进行配置。如果未配置,AzureMsiAuthentication 会查询 Azure 的实例元数据服务以获取这些详细信息。

另请参阅:

GCP-GCE 认证

gcp 认证后端允许使用现有的 GCP(Google Cloud Platform)IAM 和 GCE 凭证进行 Vault 登录。

GCP GCE(Google Compute Engine)身份验证为服务账户创建了一个 JSON Web Token(JWT)形式的签名。对于 Compute Engine 实例,JWT 是通过 GCE 元数据服务使用实例身份验证获取的。该 API 创建了一个可用于确认实例身份的 JSON Web Token。

与大多数 Vault 认证后端不同,此后端不需要首次部署或提供敏感的安全凭证(如令牌、用户名/密码、客户端证书等)。相反,它将 GCP 视为可信第三方,并使用加密签名的动态元数据信息,这些信息唯一地代表每个 GCP 服务账户。

你可以通过 GcpComputeAuthenticationOptions 来配置认证。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

GcpComputeAuthenticationOptions options = GcpComputeAuthenticationOptions.builder()
.role().build();

GcpComputeAuthentication authentication = new GcpComputeAuthentication(options,
restOperations());
}

// …
}
java

另请参阅:

GCP-IAM 认证

gcp 认证后端允许使用现有的 GCP(Google Cloud Platform)IAM 和 GCE 凭证进行 Vault 登录。

GCP IAM 认证为服务账户创建了一个 JSON Web Token (JWT) 形式的签名。服务账户的 JWT 是通过调用 GCP IAM 的 projects.serviceAccounts.signJwt API 获取的。调用者通过 GCP IAM 进行身份验证,从而证明其身份。此 Vault 后端将 GCP 视为可信第三方。

IAM 凭据可以从运行时环境中获取,也可以以 JSON 等形式从外部提供。JSON 是首选形式,因为它包含了调用 projects.serviceAccounts.signJwt 所需的项目 ID 和服务账号标识符。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

GcpIamCredentialsAuthenticationOptions options = GcpIamCredentialsAuthenticationOptions.builder()
.role().credential(GoogleCredentials.getApplicationDefault()).build();

GcpIamCredentialsAuthentication authentication = new GcpIamCredentialsAuthentication(options,
restOperations());
}

// …
}
java

GcpIamCredentialsAuthenticationOptions 需要 Google Cloud Java SDK 依赖项(com.google.cloud:google-cloud-iamcredentials),因为身份验证实现使用了 Google API 来管理凭据和 JWT 签名。

你可以通过 GcpIamCredentialsAuthenticationOptions 配置身份验证。

备注

Google 凭据需要维护 OAuth 2 令牌的生命周期。由于所有 API 都是同步的,因此 GcpIamCredentialsAuthentication 不支持 AuthenticationSteps,而 AuthenticationSteps 是响应式使用所必需的。

备注

GcpIamCredentialsAuthentication 使用了 IAM Credentials API,它是用来替代已弃用的 GcpIamAuthentication,后者使用了已弃用的 IAM API

另请参阅:

PCF 认证

pcf 认证后端允许 Vault 登录 PCF 实例。它利用了 PCF 的应用和容器身份保证

PCF 认证使用实例密钥和证书创建一个签名,该签名由 Vault 进行验证。如果签名匹配,并且可能绑定的组织/空间/应用程序 ID 也匹配,Vault 将颁发一个具有适当范围的令牌。

实例凭证可以从 CF_INSTANCE_CERTCF_INSTANCE_KEY 变量指定的文件中获取。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

PcfAuthenticationOptions options = PcfAuthenticationOptions.builder()
.role().build();

PcfAuthentication authentication = new PcfAuthentication(options,
restOperations());
}

// …
}
java

PcfAuthenticationOptions 需要 BouncyCastle 库来创建 RSA-PSS 签名。

你可以通过 PcfAuthenticationOptions 来配置认证。

另请参阅:

TLS 证书认证

cert 认证后端允许使用由 CA 签名或自签名的 SSL/TLS 客户端证书进行身份验证。

要启用 cert 认证,你需要:

  1. 使用 SSL,请参阅 [vault.client-ssl]

  2. 配置一个包含客户端证书和私钥的 Java Keystore

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

ClientCertificateAuthenticationOptions options = ClientCertificateAuthenticationOptions.builder()
.path().build();

return new ClientCertificateAuthentication(options, restOperations());
}

// …
}
java

Cubbyhole 认证

Cubbyhole 认证使用 Vault 原语来提供安全的认证工作流。Cubbyhole 认证使用令牌作为主要的登录方法。一个临时令牌用于从 Vault 的 Cubbyhole 秘密后端获取第二个登录 VaultToken。登录令牌通常具有较长的生命周期,并用于与 Vault 进行交互。登录令牌可以从包装的响应中或从 data 部分中检索。

创建封装代币

备注

创建令牌的响应包装功能需要 Vault 0.6.0 或更高版本。

示例 3. 创建和存储令牌

$ vault token-create -wrap-ttl="10m"
Key Value
--- -----
wrapping_token: 397ccb93-ff6c-b17b-9389-380b01ca2645
wrapping_token_ttl: 0h10m0s
wrapping_token_creation_time: 2016-09-18 20:29:48.652957077 +0200 CEST
wrapped_accessor: 46b6aebb-187f-932a-26d7-4f3d86a68319
shell

示例 4. 包装令牌响应的使用

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions
.builder()
.initialToken(VaultToken.of("…"))
.wrapped()
.build();

return new CubbyholeAuthentication(options, restOperations());
}

// …
}
java

使用存储的令牌

示例 5. 创建和存储令牌

$ vault token create
Key Value
--- -----
token f9e30681-d46a-cdaf-aaa0-2ae0a9ad0819
token_accessor 4eee9bd9-81bb-06d6-af01-723c54a72148
token_duration 0s
token_renewable false
token_policies [root]

$ vault token create -use-limit=2 -orphan -no-default-policy -policy=none
Key Value
--- -----
token 895cb88b-aef4-0e33-ba65-d50007290780
token_accessor e84b661c-8aa8-2286-b788-f258f30c8325
token_duration 0s
token_renewable false
token_policies [none]

$ export VAULT_TOKEN=895cb88b-aef4-0e33-ba65-d50007290780
$ vault write cubbyhole/token token=f9e30681-d46a-cdaf-aaa0-2ae0a9ad0819
shell

示例 6. 存储令牌响应的使用

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions
.builder()
.initialToken(VaultToken.of("…"))
.path("cubbyhole/token")
.build();

return new CubbyholeAuthentication(options, restOperations());
}

// …
}
java

剩余 TTL/可续期性

从 Cubbyhole 中检索到的与 TTL 非零相关联的令牌,其 TTL 从令牌创建时开始计算。该时间并不一定与应用程序启动时间相同。为了补偿初始延迟,Cubbyhole 认证会对与 TTL 非零相关联的令牌执行自查找,以检索剩余的 TTL。Cubbyhole 认证不会对没有 TTL 的封装令牌进行自查找,因为零 TTL 表示没有与之关联的 TTL。

非包装的令牌在仅检索令牌时不会提供有关可续期性和 TTL 的详细信息。自我查找将查找可续期性和剩余的 TTL。

另请参阅:

JWT 认证

配置 JWT 认证需要令牌或 JWT 供应商。你可以通过 JwtAuthenticationOptions 来配置认证。

在 Vault 端,你可以通过启用 JWT 认证后端并创建一个角色来配置 JWT 后端。你可以使用 oidc_discovery_urljwks_urljwt_validation_pubkeys 来配置 JWT 后端。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

JwtAuthenticationOptions options = JwtAuthenticationOptions.builder()
.role().jwt().path().build();

return new JwtAuthentication(options, restOperations());
}

// …
}
java

另请参阅:

Kubernetes 认证

自 0.8.3 版本起,Vault 支持使用 Kubernetes 令牌进行基于 Kubernetes 的身份验证。

使用 Kubernetes 认证需要一个 Kubernetes 服务账户令牌(Service Account Token),通常挂载在 /var/run/secrets/kubernetes.io/serviceaccount/token 路径下。该文件包含了令牌,该令牌会被读取并发送到 Vault。Vault 在登录过程中使用 Kubernetes 的 API 来验证其有效性。

配置 Kubernetes 认证至少需要提供角色名称:

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

KubernetesAuthenticationOptions options = KubernetesAuthenticationOptions.builder()
.role().jwtSupplier().build();

return new KubernetesAuthentication(options, restOperations());
}

// …
}
java

你可以通过 KubernetesAuthenticationOptions 配置认证。

另请参阅:

用户名/密码认证

用户名/密码通常是一种终端用户身份验证方案。多个 Vault 身份验证后端支持使用用户名和密码:

  • 用户名和密码 (userpass)

  • LDAP (ldap)

  • Okta (okta, 额外支持基于时间的一次性令牌)

  • RADIUS (radius)

UserPasswordAuthenticationOptions 可以与上述所有认证后端一起使用,因为所有机制的登录 API 都相似。在配置 UserPasswordAuthenticationOptions 时,请确保使用适当的认证挂载路径。

示例 7. 配置 UserPasswordAuthentication

@Configuration
class AppConfig extends AbstractVaultConfiguration {

// …

@Override
public ClientAuthentication clientAuthentication() {

UserPasswordAuthenticationOptions options = UserPasswordAuthenticationOptions.builder()
.username().password().build();

return new UserPasswordAuthentication(options, restOperations());
}

// …
}
java

另请参阅:

认证步骤

ClientAuthentication 对象描述了认证流程并执行实际的认证步骤。预组合的认证方式易于使用和配置,并与同步执行紧密绑定。

ClientAuthentication 对象不用于组合认证方法或重用常见步骤,例如将登录有效负载发布到 Vault 或从 HTTP 源检索认证输入。

认证步骤提供了常见认证活动的可重用性。通过 AuthenticationSteps 创建的步骤以功能风格描述认证流程,而将实际的认证执行留给特定的执行器。

示例 8. 存储令牌认证流程。

AuthenticationSteps.just(VaultToken.of());                              1
java
  • 仅从 VaultToken 创建 AuthenticationSteps

可以从单个输入创建单步认证流程。声明多个认证步骤的流程从 SupplierHttpRequest 开始,它们提供一个认证状态对象,该对象可用于映射或发布到 Vault 进行登录。

示例 9. AppRole 认证流程

AuthenticationSteps.fromSupplier(                                       1

() -> getAppRoleLogin(options.getRoleId(), options.getSecretId())) 2

.login("auth/{mount}/login", options.getPath()); 3
java
  • 开始声明 AuthenticationSteps,接受一个 Supplier<T>。状态对象的类型取决于 Supplier 的响应类型,该类型可以在后续步骤中进行映射。

  • 实际的 Supplier 实现。在这种情况下,创建一个 Map

  • 通过将状态对象(Map)发布到 Vault 端点以创建 Vault 令牌来执行 Vault 登录。请注意,模板变量会进行 URL 转义。

认证流程需要一个执行器来执行实际的登录操作。我们提供了两种执行器以适应不同的执行模型:

  • AuthenticationStepsExecutor 作为同步 ClientAuthentication 的直接替代品。

  • AuthenticationStepsOperator 用于响应式执行。

许多 ClientAuthentication 都带有静态工厂方法,用于为其特定于身份验证的选项创建 AuthenticationSteps

示例 10. 同步 AuthenticationSteps 执行

CubbyholeAuthenticationOptions options =
RestOperations restOperations =

AuthenticationSteps steps = CubbyholeAuthentication.createAuthenticationSteps(options);

AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(steps, restOperations);

VaultToken token = executor.login();
java

令牌生命周期

Vault 的令牌可以与生存时间相关联。通过身份验证方法获取的令牌旨在在会话活跃期间使用,并且在应用程序活跃期间不应过期。

Spring Vault 提供了一个名为 LifecycleAwareSessionManager 的会话管理器,它可以在令牌达到其最终 TTL 之前进行续期,然后执行另一次登录以获取与会话关联的下一个令牌。

根据认证方法的不同,登录可以创建两种类型的令牌:

  • VaultToken: 封装实际令牌的通用令牌。

  • LoginToken: 与可续期性/TTL 相关联的令牌。

诸如 TokenAuthentication 这样的认证方法只会创建一个 VaultToken,该令牌不携带任何可续期性或生存时间(TTL)的详细信息。LifecycleAwareSessionManager 会对该令牌进行自查找,以从 Vault 中检索其可续期性和 TTL 信息。如果启用了自查找功能,VaultToken 会定期续期。需要注意的是,VaultToken 永远不会被撤销,只有 LoginToken 会被撤销。

直接创建 LoginToken 的认证方法(所有基于登录的认证方法)已经提供了设置令牌续期所需的所有必要细节。如果会话管理器关闭,LifecycleAwareSessionManager 会撤销通过登录获得的令牌。