响应式基础设施

本节介绍使用 Spring Vault 进行响应式编程支持的基本信息。

什么是响应式编程?

简单来说,响应式编程是关于非阻塞应用程序的,这些应用程序是异步的、事件驱动的,并且需要少量线程来进行垂直扩展(即在 JVM 内)而不是水平扩展(即通过集群)。

响应式应用程序的一个关键方面是背压的概念,这是一种机制,可以确保生产者不会压倒消费者。例如,在一个从数据库到 HTTP 响应的响应式组件管道中,当 HTTP 连接过慢时,数据存储库也可以减慢或完全停止,直到网络容量释放。

响应式 Vault 客户端

Spring Vault 的响应式客户端支持建立在 可组合身份验证步骤 和 Spring 的函数式 WebClient(通过 Reactor Netty 或 Jetty)之上,它们都具有完全非阻塞、事件驱动的 HTTP 客户端。

它公开 VaultTokenSupplier 作为 VaultToken 的提供者来验证 HTTP 请求,并公开 ReactiveVaultOperations 作为主要入口点。VaultEndpointClientOptionsSSL 的核心配置在各种客户端实现中重复使用。

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

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

Vault 文档与域类之间的映射是通过委托给 WebClient 及其编解码器来完成的。

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

引用 ReactiveVaultTemplate 实例上的操作的首选方法是通过其接口 ReactiveVaultOperations

对于 ReactiveVaultTemplate 未明确公开的功能,您可以使用几种执行回调方法之一来访问底层 API。执行回调将为您提供对 WebClient 对象的引用。有关更多信息,请参阅 执行回调 部分。

现在让我们来看一些在 Spring 容器环境下如何使用 Vault 的示例。

注册和配置 Spring Vault Bean

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

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

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

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

    /**
     * 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://127.0.0.1:8200
2 此示例使用 TokenAuthentication 快速入门。有关支持的身份验证方法的详细信息,请参见 [vault.core.authentication]

会话管理

Spring Vault 需要一个令牌来验证 Vault 请求。有关身份验证的详细信息,请参见 [vault.core.authentication]。响应式客户端需要一个非阻塞令牌提供者,其契约在 VaultTokenSupplier 中定义。令牌可以是静态的,也可以通过 声明的身份验证流程 获得。Vault 登录不应在每次经过身份验证的 Vault 交互时发生,而会话令牌应在整个会话中保留。此方面由实现 ReactiveSessionManager 的会话管理器处理,例如 ReactiveLifecycleAwareSessionManager

执行回调

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

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

  • <T> T doWithVault (Function<WebClient, ? extends T> clientCallback) 使用给定的 WebClient 组成一个响应式序列,允许在没有会话上下文的情况下与 Vault 交互。

  • <T> T doWithSession (Function<WebClient, ? extends T> clientCallback) 使用给定的 WebClient 组成一个响应式序列,允许在经过身份验证的会话中与 Vault 交互。

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

reactiveVaultOperations.doWithVault(webClient -> {

    return webClient.put()
                    .uri("/sys/init")
                    .syncBody(request)
                    .retrieve()
                    .toEntity(VaultInitializationResponse.class);
});