使用 Azure Key Vault 证书在 Spring Boot 中启用 HTTPS

本教程介绍如何使用 Azure Key Vault 和 Azure 资源的托管标识通过 TLS/SSL 证书保护 Spring Boot(包括 Azure Spring Apps)应用。

生产级 Spring Boot 应用程序(无论是在云中还是本地)需要使用标准 TLS 协议对网络流量进行端到端加密。 你遇到的大多数 TLS/SSL 证书可从公共根证书颁发机构(CA)发现。 但是,有时这种发现是不可能的。 当证书不可发现时,应用必须有某种方式加载此类证书,将其呈现到入站网络连接,并从出站网络连接接受它们。

Spring Boot 应用通常通过安装证书来启用 TLS。 证书安装到运行 Spring Boot 应用的 JVM 的本地密钥存储中。 使用 Azure 上的 Spring 时,不会在本地安装证书。 相反,适用于 Microsoft Azure 的 Spring 集成提供了一种安全且无摩擦的方式,用于在 Azure Key Vault 和 Azure 资源的托管标识的帮助下启用 TLS。

显示本教程中元素交互的关系图。

重要

目前,Spring Cloud Azure 证书初学者版本 4.x 或更高版本不支持 TLS/mTLS,它们仅自动配置 Key Vault 证书客户端。 因此,如果要使用 TLS/mTLS,则无法迁移到版本 4.x。

先决条件

  • Azure 订阅 - 免费创建订阅

  • 受支持的 Java 开发工具包 (JDK) 版本 11。

  • Apache Maven 3.0 或更高版本。

  • Azure CLI

  • cURL 或类似的 HTTP 实用工具来测试功能。

  • Azure 虚拟机(VM)实例。 如果没有,请使用 az vm create 命令和 UbuntuServer 提供的 Ubuntu 映像创建启用了系统分配的托管标识的 VM 实例。 授予 Contributor 角色到系统分配的受管身份,然后将访问权限设置为 scope 到您的订阅。

  • Azure Key Vault 实例。 如果没有密钥保管库,请参阅 快速入门:使用 Azure 门户创建密钥保管库

  • Spring Boot 应用程序。 如果没有,请使用 Spring Initializr创建 Maven 项目。 请务必选择 Maven 项目,在“依赖项”下,添加 Spring Web 依赖项,然后选择 Java 版本 8 或更高版本。

重要

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

设置自签名 TLS/SSL 证书

本教程中的步骤适用于直接存储在 Azure Key Vault 中的任何 TLS/SSL 证书(包括自签名证书)。 自签名证书不适合在生产环境中使用,但对于开发和测试应用程序非常有用。

本教程使用自签名证书。 若要设置证书,请参阅 快速入门:使用 Azure 门户设置和检索 Azure Key Vault 中的证书。

注意

设置证书后,按照 分配 Key Vault 访问策略中的说明授予 VM 对 Key Vault 的访问权限。

通过 TLS/SSL 证书保护连接

现在已有一个 VM 和一个 Key Vault 实例,并已授予 VM 对 Key Vault 的访问权限。 以下部分介绍如何在 Spring Boot 应用程序中通过 Azure Key Vault 中的 TLS/SSL 证书安全地连接。 本教程演示了以下两种方案:

  • 运行具有安全入站连接的 Spring Boot 应用程序
  • 运行具有安全出站连接的 Spring Boot 应用程序

提示

在以下步骤中,代码将打包到可执行文件中,并上传到 VM。 不要忘记在 VM 中安装 OpenJDK

运行具有安全入站连接的 Spring Boot 应用程序

当入站连接的 TLS/SSL 证书来自 Azure Key Vault 时,请按照以下步骤配置应用程序:

  1. 将以下依赖项添加到 pom.xml 文件:

    <dependency>
       <groupId>com.azure.spring</groupId>
       <artifactId>azure-spring-boot-starter-keyvault-certificates</artifactId>
       <version>3.14.0</version>
    </dependency>
    
  2. application.properties 配置文件中配置 Key Vault 凭据。

    server.ssl.key-alias=<the name of the certificate in Azure Key Vault to use>
    server.ssl.key-store-type=AzureKeyVault
    server.ssl.trust-store-type=AzureKeyVault
    server.port=8443
    azure.keyvault.uri=<the URI of the Azure Key Vault to use>
    

    这些值使 Spring Boot 应用能够针对 TLS/SSL 证书执行 加载 操作,如本教程开头所述。 下表描述了属性值。

    财产 描述
    server.ssl.key-alias 的值 --name 参数的 az keyvault certificate create.
    server.ssl.key-store-type 必须是 AzureKeyVault
    server.ssl.trust-store-type 必须是 AzureKeyVault
    server.port 用于监听 HTTPS 连接的本地 TCP 端口。
    azure.keyvault.uri vaultUri 属性中的 az keyvault create. 在环境变量中保存了此值。

    密钥保管库的唯一特定属性是 azure.keyvault.uri. 应用程序在具有系统分配的托管标识,并已被授予密钥库访问权限的虚拟机上运行。 因此,应用也被授予访问权限。

    这些更改使 Spring Boot 应用能够加载 TLS/SSL 证书。 在下一步中,你将使应用能够执行 接受 TLS/SSL 证书的 操作,如本教程开头所述。

  3. 编辑启动类文件,使其包含以下内容。

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class SsltestApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SsltestApplication.class, args);
        }
    
        @GetMapping(value = "/ssl-test")
        public String inbound(){
            return "Inbound TLS is working!!";
        }
    
        @GetMapping(value = "/exit")
        public void exit() {
            System.exit(0);
        }
    
    }
    

    从未经身份验证的 REST GET 调用中调用 System.exit(0) 仅用于演示目的。 请勿在实际应用程序中使用 System.exit(0)

    这段代码说明了 礼品 本教程开头提到的操作。 以下列表突出显示了有关此代码的一些详细信息:

    • 现在有一个 @RestController 上的 SsltestApplication 类由 Spring Initializr 生成。
    • 有一个用 @GetMapping注释的方法,其中 value 用于你发出的 HTTP 调用。
    • 当浏览器向 /ssl-test 路径发出 HTTPS 请求时,inbound 方法只会返回问候语。 inbound 方法说明了服务器如何将 TLS/SSL 证书呈现给浏览器。
    • exit 方法导致 JVM 在调用时退出。 此方法便于在本教程的上下文中轻松运行示例。
  4. 运行以下命令以编译代码并将其打包到可执行的 JAR 文件中。

    mvn clean package
    
  5. 验证在 <your-resource-group-name> 允许从您的 IP 地址通过端口 22 和 8443 输入流量。 若要了解如何配置网络安全组规则以允许入站流量,请参阅 创建、更改或删除网络安全组中的 使用安全规则 部分。

  6. 将可执行文件放在 VM 上。

    cd target
    sftp azureuser@<your VM public IP address>
    put *.jar
    

    生成 Spring Boot 应用并将其上传到 VM 后,请使用以下步骤在 VM 上运行它,并使用 curl调用 REST 终结点。

  7. 使用 SSH 连接到 VM,然后运行可执行的 JAR。

    set -o noglob
    ssh azureuser@<your VM public IP address> "java -jar *.jar"
    
  8. 打开新的 Bash shell 并执行以下命令以验证服务器是否提供 TLS/SSL 证书。

    curl --insecure https://<your VM public IP address>:8443/ssl-test
    
  9. 调用 exit 路径来杀死服务器并关闭网络套接字。

    curl --insecure https://<your VM public IP address>:8443/exit
    

现在你已经看到了 负荷礼品 使用自签名的 TLS/SSL 证书进行操作,对应用程序做一些微不足道的更改,以查看 接受 行动。

运行具有安全出站连接的 Spring Boot 应用程序

在本部分中,将修改上一部分中的代码,以便出站连接的 TLS/SSL 证书来自 Azure Key Vault。 因此,在 负荷, 礼品, 和 accept Azure 密钥库满足这些要求。

  1. 将 Apache HTTP 客户端依赖项添加到 pom.xml 文件:

    <dependency>
       <groupId>org.apache.httpcomponents</groupId>
       <artifactId>httpclient</artifactId>
       <version>4.5.13</version>
    </dependency>
    
  2. 添加一个名为 ssl-test-outbound. 此终结点向自身打开 TLS 套接字,并验证 TLS 连接是否接受 TLS/SSL 证书。 将启动类的上一部分替换为以下代码。

    import java.security.KeyStore;
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import com.azure.security.keyvault.jca.KeyVaultLoadStoreParameter;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.ssl.SSLContexts;
    
    @SpringBootApplication
    @RestController
    public class SsltestApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SsltestApplication.class, args);
        }
    
        @GetMapping(value = "/ssl-test")
        public String inbound(){
            return "Inbound TLS is working!!";
        }
    
        @GetMapping(value = "/ssl-test-outbound")
        public String outbound() throws Exception {
            KeyStore azureKeyVaultKeyStore = KeyStore.getInstance("AzureKeyVault");
            KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter(
                System.getProperty("azure.keyvault.uri"));
            azureKeyVaultKeyStore.load(parameter);
            SSLContext sslContext = SSLContexts.custom()
                                               .loadTrustMaterial(azureKeyVaultKeyStore, null)
                                               .build();
    
            HostnameVerifier allowAll = (String hostName, SSLSession session) -> true;
            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, allowAll);
    
            CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();
    
            HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory();
    
            requestFactory.setHttpClient(httpClient);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            String sslTest = "https://localhost:8443/ssl-test";
    
            ResponseEntity<String> response
                = restTemplate.getForEntity(sslTest, String.class);
    
            return "Outbound TLS " +
                (response.getStatusCode() == HttpStatus.OK ? "is" : "is not")  + " Working!!";
        }
    
        @GetMapping(value = "/exit")
        public void exit() {
            System.exit(0);
        }
    
    }
    
  3. 运行以下命令以编译代码并将其打包到可执行的 JAR 文件中。

    mvn clean package
    
  4. 使用本文前面的相同 sftp 命令再次上传应用。

    cd target
    sftp <your VM public IP address>
    put *.jar
    
  5. 在 VM 上运行应用。

    set -o noglob
    ssh azureuser@<your VM public IP address> "java -jar *.jar"
    
  6. 服务器运行后,请验证服务器是否接受 TLS/SSL 证书。 在发出上一 curl 命令的同一 Bash shell 中,运行以下命令。

    curl --insecure https://<your VM public IP address>:8443/ssl-test-outbound
    

    应该看到消息 Outbound TLS is working!!

  7. 调用 exit 路径来杀死服务器并关闭网络套接字。

    curl --insecure https://<your VM public IP address>:8443/exit
    

您现在已经看到了一个简单的示例 负荷, 礼品, 和 接受 使用存储在 Azure 密钥库中的自签名 TLS/SSL 证书进行操作。

部署到 Azure Spring Apps

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

后续步骤

若要了解有关 Spring 和 Azure 的详细信息,请继续阅读 Azure 上的 Spring 文档中心。