Spring Vault – Spring的金库

最后修改: 2018年 9月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

HashiCorp’s Vault is a tool to store and secure secrets. Vault, in general, solves the software development security problem of how to manage secrets. To learn more about it, check out our article here.

HashiCorp的Vault是一个存储和保护秘密的工具。总的来说,Vault解决了如何管理秘密的软件开发安全问题。要了解更多关于它的信息,请查看我们的文章

Spring Vault provides Spring abstractions to the HashiCorp’s Vault.

Spring Vault为HashiCorp的Vault提供Spring抽象。

In this tutorial, we’ll go over an example of how to store and retrieve secrets from the Vault.

在本教程中,我们将举例说明如何存储和检索保险库中的秘密。

2. Maven Dependencies

2.Maven的依赖性

To start with, let’s take a look at the dependencies we need to start working with Spring Vault:

首先,让我们看一下我们开始使用Spring Vault所需要的依赖性。

<dependencies>
    <dependency>
        <groupId>org.springframework.vault</groupId>
        <artifactId>spring-vault-core</artifactId>
        <version>2.3.2</version>
    </dependency>
</dependencies>

The latest version of spring-vault-core can be found on Maven Central.

最新版本的spring-vault-core可以在Maven中心找到。

3. Configuring Vault

3.配置保险库

Let’s now go through the steps needed to configure Vault.

现在让我们来看看配置Vault所需的步骤。

3.1. Creating a VaultTemplate

3.1.创建一个VaultTemplate

To secure our secrets, we’ll have to instantiate a VaultTemplate for which we need VaultEndpoint and TokenAuthentication instances:

为了保护我们的秘密,我们必须实例化一个VaultTemplate,为此我们需要VaultEndpointTokenAuthentication实例。

VaultTemplate vaultTemplate = new VaultTemplate(new VaultEndpoint(), 
  new TokenAuthentication("00000000-0000-0000-0000-000000000000"));

3.2. Creating a VaultEndpoint

3.2.创建一个VaultEndpoint

There’re a few ways to instantiate VaultEndpoint. Let’s take a look at some of them.

有几种方法可以实例化VaultEndpoint。让我们看一下其中的一些。

The first one is to simply instantiate it using a default constructor, which will create a default endpoint pointing to http://localhost:8200:

第一种是简单地使用默认构造函数将其实例化,这将创建一个默认的端点,指向http://localhost:8200:

VaultEndpoint endpoint = new VaultEndpoint();

Another way is to create a VaultEndpoint by specifying Vault’s host and port:

另一种方法是通过指定Vault的主机和端口来创建一个VaultEndpoint

VaultEndpoint endpoint = VaultEndpoint.create("host", port);

And finally, we can also create it from the Vault URL:

最后,我们也可以从Vault URL中创建它。

VaultEndpoint endpoint = VaultEndpoint.from(new URI("vault uri"));

There are a few things to notice here – Vault will be configured with a root token of 00000000-0000-0000-0000-000000000000 to run this application.

这里有几件事需要注意–Vault将配置一个根令牌00000000-00000000-00000000来运行这个应用程序。

In our example, we’ve used  TokenAuthentication, but there are other authentication methods supported as well.

在我们的例子中,我们使用了TokenAuthentication,但也有其他认证方法支持。

4. Configuring Vault Beans Using Spring

4.使用Spring配置Vault Bean

With Spring, we can configure the Vault in a couple of ways. One is by extending the AbstractVaultConfiguration, and the other one is by using EnvironmentVaultConfiguration which makes use of Spring’s environment properties.

使用Spring,我们可以通过几种方式配置Vault。一种是通过扩展AbstractVaultConfiguration,另一种是通过使用EnvironmentVaultConfiguration,它利用了Spring的环境属性。

We’ll now go over both ways.

我们现在就来看看这两种方式。

4.1. Using AbstractVaultConfiguration

4.1.使用AbstractVaultConfiguration

Let’s create a class that extends AbstractVaultConfiguration, to configure Spring Vault:

让我们创建一个扩展于AbstractVaultConfiguration的类,来配置Spring Vault。

@Configuration
public class VaultConfig extends AbstractVaultConfiguration {

    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("00000000-0000-0000-0000-000000000000");
    }

    @Override
    public VaultEndpoint vaultEndpoint() {
        return VaultEndpoint.create("host", 8020);
    }
}

This approach is similar to what we’ve seen in the previous section. What’s different is that we’ve used Spring Vault to configure Vault beans by extending the abstract class AbstractVaultConfiguration.

这种方法与我们在上一节中看到的类似。不同的是,我们通过扩展抽象类AbstractVaultConfiguration,使用Spring Vault来配置Vault Bean。

We just have to provide the implementation to configure VaultEndpoint and ClientAuthentication.

我们只需提供配置VaultEndpointClientAuthentication的实现。

4.2. Using EnvironmentVaultConfiguration

4.2.使用EnvironmentVaultConfiguration

We can also configure Spring Vault using the EnviromentVaultConfiguration:

我们还可以使用EnviromentVaultConfiguration来配置Spring Vault。

@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}

EnvironmentVaultConfiguration makes use of Spring’s PropertySource to configure Vault beans. We just need to supply the properties file with some acceptable entries.

EnvironmentVaultConfiguration利用Spring的PropertySource来配置Vault Bean。我们只需要在属性文件中提供一些可接受的条目。

More information on all of the predefined properties can be found in the official documentation.

关于所有预定义属性的更多信息可以在官方文档中找到。

To configure the Vault, we need at least a couple of properties:

为了配置Vault,我们至少需要几个属性。

vault.uri=https://localhost:8200
vault.token=00000000-0000-0000-0000-000000000000

5. Securing Secrets

5 确保秘密的安全

We’ll create a simple Credentials class that maps to username and password:

我们将创建一个简单的Credentials类,映射到用户名和密码。

public class Credentials {

    private String username;
    private String password;
    
    // standard constructors, getters, setters
}

Now, let’s see how we can secure our Credentials object using the VaultTemplate:

现在,让我们看看如何使用Credentials对象保护我们的VaultTemplate:

Credentials credentials = new Credentials("username", "password");
vaultTemplate.write("secret/myapp", credentials);

With these lines completed, our secrets are now stored.

完成这些行后,我们的秘密就被储存起来了。

Next, we’ll see how to access them.

接下来,我们将看到如何访问它们。

6. Accessing Secrets

6.获取秘密

We can access the secured secrets using the read() method in VaultTemplate, which returns the VaultResponseSupport as a response:

我们可以使用VaultTemplate中的read() 方法访问安全的秘密,该方法将VaultResponseSupport作为一个响应返回。

VaultResponseSupport<Credentials> response = vaultTemplate
  .read("secret/myapp", Credentials.class);
String username = response.getData().getUsername();
String password = response.getData().getPassword();

Our secret values are now ready.

我们的秘密价值现在已经准备好了。

7. Vault Repositories

7.保险库储存库

Vault repository is a handy feature that comes with Spring Vault 2.0. It applies Spring Data’s repository concept on top of Vault.

Vault仓库是Spring Vault 2.0中的一个方便的功能。它将Spring Data的资源库概念应用于Vault之上

Let’s dig deep to see how to use this new feature in practice.

让我们深入挖掘,看看如何在实践中使用这个新功能。

7.1. @Secret and @Id Annotations

7.1.@Secret@Id 注释

Spring provides these two annotations to mark the objects we want to persist inside Vault.

Spring提供了这两个注解来标记我们想在Vault中持久化的对象。

So first, we need to decorate our domain type Credentials:

所以首先,我们需要装饰我们的域类型Credentials

@Secret(backend = "credentials", value = "myapp")
public class Credentials {

    @Id
    private String username;
    // Same code
]

The value attribute of the @Secret annotation serves to distinguish the domain type. The backend attribute denotes the secret backend mount.

@Secret注解的value属性用于区分域类型。backend属性表示秘密后端装载。

On the other hand, @Id simply demarcates the identifier of our object.

另一方面,@Id简单地划分了我们对象的标识符。

7.2. Vault Repository

7.2.金库库房

Now, let’s define a repository interface that uses our domain object Credentials:

现在,让我们定义一个存储库接口,使用我们的域对象Credentials

public interface CredentialsRepository extends CrudRepository<Credentials, String> {
}

As we can see, our repository extends CrudRepository which provides basic CRUD and query methods.

我们可以看到,我们的仓库扩展了CrudRepository,提供了基本的CRUD和查询方法

Next, let’s inject CredentialsRepository into CredentialsService and implement some CRUD methods:

接下来,让我们将CredentialsRepository注入到CredentialsService中,并实现一些CRUD方法。

public class CredentialsService {

    @Autowired
    private CredentialsRepository credentialsRepository;

    public Credentials saveCredentials(Credentials credentials) {
        return credentialsRepository.save(credentials);
    }

    public Optional<Credentials> findById(String username) {
        return credentialsRepository.findById(username);
    }
}

Now that we have added all the missing pieces of the puzzle, let’s confirm that everything works as excepted using test cases.

现在我们已经添加了拼图中所有缺失的部分,让我们用测试用例来确认一切都按规定工作。

First, let’s start with a test case for the save() method:

首先,让我们从save()方法的测试案例开始。

@Test
public void givenCredentials_whenSave_thenReturnCredentials() {
    // Given
    Credentials credentials = new Credentials("login", "password");
    Mockito.when(credentialsRepository.save(credentials))
      .thenReturn(credentials);

    // When
    Credentials savedCredentials = credentialsService.saveCredentials(credentials);

    // Then
    assertNotNull(savedCredentials);
    assertEquals(savedCredentials.getUsername(), credentials.getUsername());
    assertEquals(savedCredentials.getPassword(), credentials.getPassword());
}

Lastly, let’s confirm the findById() method with a test case:

最后,让我们用一个测试案例来确认findById()方法。

@Test
public void givenId_whenFindById_thenReturnCredentials() {
    // Given
    Credentials credentials = new Credentials("login", "p@ssw@rd");
    Mockito.when(credentialsRepository.findById("login"))
      .thenReturn(Optional.of(credentials));

    // When
    Optional<Credentials> returnedCredentials = credentialsService.findById("login");

    // Then
    assertNotNull(returnedCredentials);
    assertNotNull(returnedCredentials.get());
    assertEquals(returnedCredentials.get().getUsername(), credentials.getUsername());
    assertEquals(returnedCredentials.get().getPassword(), credentials.getPassword());
}

8. Conclusion

8.结论

In this article, we’ve learned about the basics of Spring Vault with an example showing how the Vault works in typical scenarios.

在这篇文章中,我们已经了解了Spring Vault的基础知识,并通过一个例子展示了Vault在典型场景中的工作情况.

As usual, the source code presented here can be found over on GitHub.

像往常一样,这里介绍的源代码可以在GitHub上找到over