在 Spring Boot 应用程序中从 Azure 密钥库加载机密

本教程介绍如何在 Spring Boot 应用程序中使用密钥库来保护敏感配置数据并从密钥库检索配置属性。 Key Vault 提供了安全的通用存储机密,例如密码和数据库连接字符串。

先决条件

重要

完成本文中的步骤需要 Spring Boot 2.5 或更高版本。

将机密设置为 Azure 密钥库

本教程介绍如何从 Spring Boot 应用程序中的密钥库读取数据库凭据。 若要从密钥库读取凭据,应首先将数据库凭据存储在密钥库中。

若要将 H2 数据库的 URL 存储为密钥库中的新机密,请参阅快速入门:使用 Azure 门户 设置和检索 Azure 密钥库中的机密。 在本教程中,你将设置一个具有名称和 h2url 值的 jdbc:h2:~/testdb;user=sa;password=password机密。

注意

设置机密后,按照分配密钥库访问策略中的说明授予应用对密钥库的访问权限。

从 Azure 密钥库读取机密

现在,数据库凭据已存储在密钥库中,可以使用 Spring Cloud Azure 检索它们。

若要安装 Spring Cloud Azure 密钥库 Starter 模块,请将以下依赖项添加到pom.xml文件中:

  • Spring Cloud Azure 材料清单(BOM):

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>com.azure.spring</groupId>
          <artifactId>spring-cloud-azure-dependencies</artifactId>
          <version>5.11.0</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>
    

    注意

    如果使用 Spring Boot 2.x,请确保将 spring-cloud-azure-dependencies 版本设置为 4.17.0。 此材料清单(BOM)应在pom.xml文件的部分中进行配置<dependencyManagement>。 这可确保所有 Spring Cloud Azure 依赖项都使用相同的版本。 有关用于此 BOM 的版本的详细信息,请参阅 我应使用哪个版本的 Spring Cloud Azure。

  • Spring Cloud Azure 密钥库 Starter 项目:

    <dependency>
      <groupId>com.azure.spring</groupId>
      <artifactId>spring-cloud-azure-starter-keyvault</artifactId>
    </dependency>
    

Spring Cloud Azure 有多种从密钥库读取机密的方法。 可以单独使用以下方法,或将它们合并为不同的用例:

  • 使用 Azure SDK 进行密钥库。
  • 使用 Spring KeyVault PropertySource

使用 Azure SDK 进行密钥库

用于密钥库的 Azure SDK 提供SecretClient用于管理密钥库中的机密。

以下代码示例演示如何使用 SecretClient Azure 密钥库检索 H2 数据库凭据。

若要从 密钥库 中使用 Azure SDK 读取机密,请执行以下步骤配置应用程序:

  1. application.properties 配置文件中配置密钥库终结点。

    spring.cloud.azure.keyvault.secret.endpoint=https://<your-keyvault-name>.vault.azure.net/
    
  2. SecretClient Spring 应用程序中注入 bean 并使用 getSecret 方法检索机密,如以下示例所示:

    import com.azure.security.keyvault.secrets.SecretClient;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SecretClientApplication implements CommandLineRunner {
    
        // Spring Cloud Azure will automatically inject SecretClient in your ApplicationContext.
        private final SecretClient secretClient;
    
        public SecretClientApplication(SecretClient secretClient) {
            this.secretClient = secretClient;
        }
    
        public static void main(String[] args) {
            SpringApplication.run(SecretClientApplication.class, args);
        }
    
        @Override
        public void run(String... args) {
            System.out.println("h2url: " + secretClient.getSecret("h2url").getValue());
        }
    }
    

    提示

    在本教程中,配置或代码中没有身份验证操作。 但是,连接到 Azure 服务需要身份验证。 要完成身份验证,需要使用 Azure 标识。 Spring Cloud Azure 使用 DefaultAzureCredentialAzure 标识库提供的帮助获取凭据,而无需进行任何代码更改。

    DefaultAzureCredential 支持多种身份验证方法,并确定应在运行时使用哪种方法。 此方法使应用能够在不同环境(如本地环境和生产环境)中使用不同的身份验证方法,而无需实现特定于环境的代码。 有关详细信息,请参阅 DefaultAzureCredential

    若要在本地开发环境中完成身份验证,可以使用 Azure CLI、Visual Studio Code、PowerShell 或其他方法。 有关详细信息,请参阅 Java 开发环境中的 Azure 身份验证。 若要在 Azure 托管环境中完成身份验证,建议使用用户分配的托管标识。 有关详细信息,请参阅什么是 Azure 资源的托管标识?

  3. 启动应用程序。 你将看到类似于以下示例的日志:

    h2url: jdbc:h2:~/testdb;user=sa;password=password
    

你可以自己构建 SecretClient 豆子,但这个过程很复杂。 在 Spring Boot 应用程序中,必须管理属性、了解生成器模式,并将客户端注册到 Spring 应用程序上下文。 以下代码示例演示如何生成 SecretClient bean:

import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.security.keyvault.secrets.SecretClient;
import com.azure.security.keyvault.secrets.SecretClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SecretClientConfiguration {

    @Bean
    public SecretClient createSecretClient() {
        return new SecretClientBuilder()
            .vaultUrl("https://<your-key-vault-url>.vault.azure.net/")
            .credential(new DefaultAzureCredentialBuilder().build())
            .buildClient();
    }

}

以下列表显示了此代码不灵活或正常的原因之一:

  • 密钥库终结点已硬编码。
  • 如果用于@Value从 Spring 环境获取配置,则 application.properties 文件中不能有 IDE 提示
  • 如果有微服务方案,则必须在每个项目中复制代码,并且很容易出错且难以保持一致。

幸运的是,使用 Spring Cloud Azure 不需要自行构建 SecretClient 豆子。 相反,可以直接注入SecretClient和使用已熟悉的配置属性来配置密钥库。 有关详细信息,请参阅 配置示例

Spring Cloud Azure 还为不同方案提供以下全局配置。 有关详细信息,请参阅 Spring Cloud Azure 开发人员指南Azure 服务 SDK 的全局配置部分。

  • 代理选项。
  • 重试选项。
  • HTTP 传输客户端选项。

还可以连接到不同的 Azure 云。 有关详细信息,请参阅连接到不同的 Azure 云

使用 Spring 密钥库 PropertySource

前面的部分介绍了如何在应用程序启动后在读取机密时使用SecretClientCommandLineRunner。 但是,在 Spring Boot 应用程序中,在应用程序启动之前需要读取机密。 例如,在应用程序启动之前,需要数据源密码属性。 如果想要将数据源密码存储在密钥库中,并且仍使用 Spring 自动配置来获取数据源,则前面的方案将不起作用。

在这种情况下,Spring Cloud Azure 提供 Spring 环境集成,用于在生成应用程序上下文之前从密钥库加载机密。 可以使用机密在 Spring 应用程序上下文初始化期间构造和配置 bean。 此方法是访问密钥库机密的透明方法,无需更改代码。

以下代码示例演示如何使用PropertySource检索 H2 数据库凭据从 Azure 密钥库生成数据源。

若要从 密钥库检索 H2 数据库的 URL 并使用 Spring Data JPA 存储 H2 数据库中的数据,请执行以下步骤配置应用程序:

  1. 将以下密钥库终结点和数据源属性添加到 application.properties 配置文件。

    logging.level.org.hibernate.SQL=DEBUG
    
    spring.cloud.azure.keyvault.secret.property-sources[0].endpoint=https://<your-keyvault-name>.vault.azure.net/
    spring.datasource.url=${h2url}
    
    spring.jpa.hibernate.ddl-auto=create-drop
    spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
    

    提示

    有关 Spring Cloud Azure 属性配置的示例,请参阅 Spring Cloud Azure 开发人员指南“配置示例”部分。

    提示

    此示例是使用 H2 数据库的简单数据库方案。 建议在生产环境中使用 Azure Database for MySQL 或 Azure Database for PostgreSQL,并在 Azure 密钥库中存储数据库 URL、用户名和密码。 如果想要避免密码,则无密码连接是一个不错的选择。 有关详细信息,请参阅 Azure 服务的无密码连接。

  2. 创建新的 Todo Java 类。 此类是映射到 JPA 自动创建的表的 todo 域模型。 以下代码将 getters 忽略和 setters 方法。

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    @Entity
    public class Todo {
    
        public Todo() {
        }
    
        public Todo(String description, String details, boolean done) {
            this.description = description;
            this.details = details;
            this.done = done;
        }
    
        @Id
        @GeneratedValue
        private Long id;
    
        private String description;
    
        private String details;
    
        private boolean done;
    
    }
    
  3. 编辑启动类文件以显示以下内容。

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.event.ApplicationReadyEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    import java.util.stream.Stream;
    
    @SpringBootApplication
    public class KeyvaultApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(KeyvaultApplication.class, args);
        }
    
        @Bean
        ApplicationListener<ApplicationReadyEvent> basicsApplicationListener(TodoRepository repository) {
            return event->repository
                .saveAll(Stream.of("A", "B", "C").map(name->new Todo("configuration", "congratulations, you have set up "
                    + "correctly!", true)).toList())
                .forEach(System.out::println);
        }
    
    }
    
    interface TodoRepository extends JpaRepository<Todo, Long> {
    
    }
    
  4. 启动应用程序。 应用程序将从密钥库检索 H2 数据库的 URL,然后连接到 H2 数据库,并将数据存储到数据库。 你将看到类似于以下示例的日志:

    2023-01-13 15:51:35.498 DEBUG 5616 --- [main] org.hibernate.SQL: insert into todo (description, details, done, id) values (?, ?, ?, ?)
    com.contoso.keyvault.Todo@1f
    

部署到 Azure Spring Apps

现在,你已在本地运行 Spring Boot 应用程序,现在可以将其移动到生产环境。 使用 Azure Spring Apps 可以轻松地将 Spring Boot 应用程序部署到 Azure,而无需进行任何代码更改。 该服务管理 Spring 应用程序的基础结构,让开发人员可以专注于代码。 Azure Spring Apps 可以通过以下方法提供生命周期管理:综合性监视和诊断、配置管理、服务发现、CI/CD 集成、蓝绿部署等。 若要将应用程序部署到 Azure Spring Apps,请参阅将 第一个应用程序部署到 Azure Spring Apps

后续步骤