VaultTemplate 简介

位于 org.springframework.vault.core 包中的 VaultTemplate 类是 Spring Vault 支持的核心类,它提供了一套丰富的功能集来与 Vault 进行交互。该模板提供了方便的操作来读取、写入和删除 Vault 中的数据,并提供域对象与 Vault 数据之间的映射。

配置完成后,VaultTemplate 是线程安全的,可以在多个实例中重复使用。

Vault 文档与域类之间的映射是通过委托给 RestTemplate 来完成的。Spring Web 支持提供了映射基础设施。

VaultTemplate 类实现了 VaultOperations 接口。在尽可能的情况下,VaultOperations 上的方法以 Vault API 上可用的方法命名,以使 API 对熟悉 API 和 CLI 的现有 Vault 开发人员来说更加熟悉。例如,您会发现诸如“write”、“delete”、“read”和“revoke”之类的方法。设计目标是尽可能轻松地在使用 Vault API 和 VaultOperations 之间进行转换。两个 API 之间的一个主要区别是 VaultOperations 可以传递域对象而不是 JSON 键值对。

引用 VaultTemplate 实例上的操作的首选方式是通过其接口 VaultOperations

虽然 VaultTemplate 上有很多便捷方法可以帮助您轻松执行常见任务,但如果您需要直接访问 Vault API 来访问 VaultTemplate 未明确公开的功能,您可以使用几种执行回调方法来访问底层 API。执行回调将为您提供对 RestOperations 对象的引用。有关更多信息,请参见部分 执行回调

现在让我们看看如何在 Spring 容器的上下文中使用 Vault 的示例。

注册和配置 Spring Vault bean

使用 Spring Vault 不需要 Spring 上下文。但是,在托管上下文中注册的 VaultTemplateSessionManager 实例将参与 Spring IoC 容器提供的 生命周期事件。这对于在应用程序关闭时处置活动 Vault 会话很有用。您还可以从在整个应用程序中重用相同的 VaultTemplate 实例中受益。

Spring Vault 带有一个支持配置类,它提供用于在 Spring 上下文中使用的 bean 定义。应用程序配置类通常从 AbstractVaultConfiguration 扩展,并且需要提供特定于环境的额外详细信息。

AbstractVaultConfiguration 扩展需要实现 ` VaultEndpoint vaultEndpoint()` 和 ClientAuthentication clientAuthentication() 方法。

示例 1. 使用基于 Java 的 bean 元数据注册 Spring Vault 对象
@Configuration
public class AppConfig extends AbstractVaultConfiguration {

    /**
     * Specify an endpoint for connecting to Vault.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return new VaultEndpoint();                            (1)
    }

    /**
     * Configure a client authentication.
     * Please consider a more secure authentication method
     * for production use.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("…");                   (2)
    }
}
1 创建一个新的 VaultEndpoint,默认指向 https://localhost:8200
2 此示例使用 TokenAuthentication 快速入门。有关支持的身份验证方法的详细信息,请参见 [vault.core.authentication]
示例 2. 注册 Spring Vault 应用注入属性
@Configuration
public class AppConfig extends AbstractVaultConfiguration {

    @Value("${vault.uri}")
    URI vaultUri;

    /**
     * Specify an endpoint that was injected as URI.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return VaultEndpoint.from(vaultUri);                          (1)
    }

    /**
     * Configure a Client Certificate authentication.
     * {@link RestOperations} can be obtained from {@link #restOperations()}.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new ClientCertificateAuthentication(restOperations()); (2)
    }
}
1 VaultEndpoint 可以使用各种工厂方法构建,例如 from(URI uri)VaultEndpoint.create(String host, int port)
2 ClientAuthentication 方法的依赖项可以从 AbstractVaultConfiguration 获取,也可以由您的配置提供。
在某些情况下,创建自定义配置类可能很麻烦。查看 EnvironmentVaultConfiguration,它允许使用来自现有属性源和 Spring 的 Environment 的属性进行配置。在 使用 EnvironmentVaultConfiguration 中了解更多信息。

会话管理

Spring Vault 需要一个 ClientAuthentication 来登录并访问 Vault。有关身份验证的详细信息,请参见 [vault.core.authentication]。Vault 登录不应在每次经过身份验证的 Vault 交互时发生,而应在整个会话中重复使用。这方面由 SessionManager 实现处理。SessionManager 决定它获取令牌的频率、撤销和续订。Spring Vault 附带两个实现

  • SimpleSessionManager:仅从提供的 ClientAuthentication 获取令牌,不进行刷新和撤销

  • LifecycleAwareSessionManager:此 SessionManager 如果令牌可续订,则安排令牌续订,并在处置时撤销登录令牌。续订使用 AsyncTaskExecutor 安排。如果使用 AbstractVaultConfiguration,则默认情况下会配置 LifecycleAwareSessionManager

使用 EnvironmentVaultConfiguration

Spring Vault 包含 EnvironmentVaultConfiguration,它可以从 Spring 的 Environment 和一组预定义的属性键配置 Vault 客户端。EnvironmentVaultConfiguration 支持常用的配置。其他配置可以通过从最合适的配置类派生来支持。将 EnvironmentVaultConfiguration@Import(EnvironmentVaultConfiguration.class) 结合使用到现有的基于 Java 的配置类中,并通过 Spring 的任何 PropertySource 提供配置属性。

示例 3. 使用 EnvironmentVaultConfiguration 与属性文件
基于 Java 的配置类
@PropertySource("vault.properties")
@Import(EnvironmentVaultConfiguration.class)
public class MyConfiguration{
}
vault.properties
vault.uri=https://localhost:8200
vault.token=00000000-0000-0000-0000-000000000000

属性键

  • Vault URI: vault.uri

  • SSL 配置

    • 密钥库资源: vault.ssl.key-store (可选)

    • 密钥库密码: vault.ssl.key-store-password (可选)

    • 密钥库类型: vault.ssl.key-store-type (可选,通常为 jks,也支持 pem)

    • 信任库资源: vault.ssl.trust-store (可选)

    • 信任库密码: vault.ssl.trust-store-password (可选)

    • 信任库类型: vault.ssl.trust-store-type (可选,通常为 jks,也支持 pem)

    • 启用的 SSL/TLS 协议: vault.ssl.enabled-protocols (自 2.3.2 版本起,可选,协议之间用逗号分隔)

    • 启用的 SSL/TLS 密码套件: vault.ssl.enabled-cipher-suites (自 2.3.2 版本起,可选,密码套件之间用逗号分隔)

  • 身份验证方法: vault.authentication (默认值为 TOKEN,支持的身份验证方法包括:TOKENAPPIDAPPROLEAWS_EC2AWS_IAMAZURECERTCUBBYHOLEKUBERNETES)

身份验证特定的属性键

  • Vault 令牌: vault.token

  • AppId 路径: vault.app-id.app-id-path (默认值为 app-id)

  • AppId: vault.app-id.app-id

  • UserId: vault.app-id.user-idMAC_ADDRESSIP_ADDRESS 使用 MacAddressUserId,分别使用 IpAddressUserId 用户 ID 机制。任何其他值都将与 StaticUserId 一起使用。

  • AppRole 路径: vault.app-role.app-role-path (默认值为 approle)

  • RoleId: vault.app-role.role-id

  • SecretId: vault.app-role.secret-id (可选)

  • AWS EC2 路径: vault.aws-ec2.aws-ec2-path (默认值为 aws-ec2)

  • 角色:vault.aws-ec2.role

  • 角色 ID:vault.aws-ec2.role-id(**已弃用:** 请改用 vault.aws-ec2.role

  • 身份文档 URL:vault.aws-ec2.identity-document(默认值为 169.254.169.254/latest/dynamic/instance-identity/pkcs7

  • 角色:vault.aws-iam.role

无配置选项。

  • 初始 Vault 令牌:vault.token

  • Kubernetes 路径:vault.kubernetes.kubernetes-path(默认值为 kubernetes

  • 角色:vault.kubernetes.role

  • 服务帐户令牌文件路径:vault.kubernetes.service-account-token-file(默认值为 /var/run/secrets/kubernetes.io/serviceaccount/token

执行回调

所有 Spring 模板类的一个常见设计特征是,所有功能都路由到其中一个模板的执行回调方法中。这有助于确保异常和可能需要的任何资源管理以一致的方式执行。虽然在 JDBC 和 JMS 的情况下,这比 Vault 更需要,但它仍然提供了一个用于访问和记录的单一位置。因此,使用执行回调是访问 Vault API 以执行我们尚未在 VaultTemplate 上公开为方法的非通用操作的首选方式。

以下是执行回调方法的列表。

  • <T> T doWithVault (RestOperationsCallback<T> callback) 执行给定的 RestOperationsCallback,允许在不使用会话的情况下使用 RestOperations 与 Vault 交互。

  • <T> T doWithSession (RestOperationsCallback<T> callback) 执行给定的 RestOperationsCallback,允许在经过身份验证的会话中与 Vault 交互。

以下是一个使用 ClientCallback 初始化 Vault 的示例

vaultOperations.doWithVault(new RestOperationsCallback<VaultInitializationResponse>() {

  @Override
  public VaultInitializationResponse doWithRestOperations(RestOperations restOperations) {

    ResponseEntity<VaultInitializationResponse> exchange = restOperations
                       .exchange("/sys/init", HttpMethod.PUT,
                                 new HttpEntity<Object>(request),
                                 VaultInitializationResponse.class);

    return exchange.getBody();
    }
});