Reactive Infrastructure
This section covers basic information on the reactive programming support using Spring Vault.
What is Reactive Programming?
In plain terms reactive programming is about non-blocking applications that are asynchronous and event-driven and require a small number of threads to scale vertically (i.e. within the JVM) rather than horizontally (i.e. through clustering).
A key aspect of reactive applications is the concept of backpressure which is a mechanism to ensure producers don’t overwhelm consumers. For example in a pipeline of reactive components extending from the database to the HTTP response when the HTTP connection is too slow the data repository can also slow down or stop completely until network capacity frees up.
Reactive Vault Client
Spring Vault’s reactive client support is built on top of composable authentication steps and Spring’s functional WebClient
via Reactor Netty or Jetty, which feature both a fully non-blocking, event-driven HTTP client.
It exposes VaultTokenSupplier
as supplier of VaultToken
to authenticate HTTP requests
and ReactiveVaultOperations
as the primary entry point.
The core configuration of
VaultEndpoint
, ClientOptions
and SSL are reused across the
various client implementation.
The class ReactiveVaultTemplate
, located in the package org.springframework.vault.core
,
is the central class of the Spring’s reactive Vault support providing a rich feature set to
interact with Vault. The template offers convenience operations to read, write and
delete data in Vault and provides a mapping between your domain objects and Vault data.
Once configured, ReactiveVaultTemplate is thread-safe and can be reused across
multiple instances.
|
The mapping between Vault documents and domain classes is done by delegating to
WebClient
and its codecs.
The ReactiveVaultTemplate
class implements the interface ReactiveVaultOperations
.
In as much as possible, the methods on ReactiveVaultOperations
are named after methods
available on the Vault API to make the API familiar to existing Vault developers
who are used to the API and CLI. For example, you will find methods such as
"write", "delete", and "read".
The design goal was to make it as easy as possible to transition between
the use of the Vault API and ReactiveVaultOperations
. A major difference in between
the two APIs is that ReactiveVaultOperations
can be passed domain objects instead of
JSON Key-Value pairs.
The preferred way to reference the operations on ReactiveVaultTemplate instance
is via its interface ReactiveVaultOperations .
|
Functionality not explicitly exposed by the ReactiveVaultTemplate
you can use one of
several execute callback methods to access underlying APIs. The execute callbacks
will give you a reference to a WebClient
object.
Please see the section Execution Callbacks for more information.
Now let’s look at examples of how to work with Vault in the context of the Spring container.
Registering and configuring Spring Vault beans
Using Spring Vault does not require a Spring Context. However, instances of
ReactiveVaultTemplate
and VaultTokenSupplier
registered inside a managed context will participate
in lifecycle events
provided by the Spring IoC container. This is useful to dispose active Vault sessions upon
application shutdown. You also benefit from reusing the same ReactiveVaultTemplate
instance across your application.
Spring Vault comes with a supporting configuration class that provides bean definitions
for use inside a Spring context. Application configuration
classes typically extend from AbstractVaultConfiguration
and are required to
provide additional details that are environment specific.
Extending from AbstractVaultConfiguration
requires to implement
` VaultEndpoint vaultEndpoint()` and ClientAuthentication clientAuthentication()
methods.
@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 | Create a new VaultEndpoint that points by default to https://localhost:8200 . |
2 | This sample uses TokenAuthentication to get started quickly.
See [vault.core.authentication] for details on supported authentication methods. |
Session Management
Spring Vault requires a token to authenticate Vault requests.
See [vault.core.authentication] on details regarding authentication.
The reactive client requires a non-blocking token supplier whose contract is defined
in VaultTokenSupplier
. Tokens can be static or obtained through a
declared authentication flow.
Vault login should not occur on each authenticated Vault interaction but
the session token should be kept across a session.
This aspect is handled by a session manager implementing ReactiveSessionManager
, such as ReactiveLifecycleAwareSessionManager
.
Execution callbacks
One common design feature of all Spring template classes is that all functionality
is routed into one of the templates execute callback methods. This helps ensure
that exceptions and any resource management that maybe required are performed
consistency. While this was of much greater need in the case of JDBC and JMS
than with Vault, it still offers a single spot for access and logging to occur.
As such, using the execute callback is the preferred way to access the Vault API
to perform uncommon operations that we’ve not exposed as methods on ReactiveVaultTemplate
.
Here is a list of execute callback methods.
-
<T> T
doWithVault(Function<WebClient, ? extends T> clientCallback)
Composes a reactive sequence the givenWebClient
, allows to interact with Vault without a session context. -
<T> T
doWithSession(Function<WebClient, ? extends T> clientCallback)
Composes a reactive sequence the givenWebClient
, allows to interact with Vault in an authenticated session.
Here is an example that uses the callback to initialize Vault:
reactiveVaultOperations.doWithVault(webClient -> {
return webClient.put()
.uri("/sys/init")
.syncBody(request)
.retrieve()
.toEntity(VaultInitializationResponse.class);
});