ZetCode

Spring Boot HTTPS

最后修改于 2023 年 7 月 24 日

在本文中,我们将展示如何在 Spring Boot 应用程序中使用 HTTPS 设置安全通信。

Spring 是一个流行的 Java 应用程序框架,而 Spring Boot 是 Spring 的一个演进,它有助于轻松创建独立的、生产级的基于 Spring 的应用程序。

HTTPS

超文本传输安全协议 (HTTPS) 是一种用于互联网通信的安全通信协议。它确保数据完整性和数据保密性。它是 HTTP 协议的扩展。在 HTTPS 中,通信协议使用传输层安全协议 (TLS) 或(过去)安全套接字层 (SSL) 进行加密。该协议通常被称为 HTTP over TLS 或 HTTP over SSL。

SSL(安全套接字层)是一种行业标准协议,通过保护在系统之间发送的所有敏感数据来确保互联网连接的安全。它防止入侵者读取和修改任何传输的信息。TLS(传输层安全)是 SSL 的一个更近期的安全版本。如今,由证书颁发机构提供的证书仅基于 TLS。这两个术语通常可以互换使用。

在 Java 中,truststore 通常用于存储受信任实体的证书。它维护一个由其信任的所有受信任方的证书组成的存储库。Keystore 用于存储服务器密钥(公钥和私钥)以及签名证书。

创建 TLS 证书

我们可以从认证机构 (CA) 获取 TLS 证书,例如 Verison、Symantec 或 Digicert。这需要时间和金钱。(有一个流行的免费自动化认证机构称为 Let's Encrypt。)另一种选择,非常适合开发目的,是创建自签名证书。

在本文中,我们使用自签名证书。

注意: 使用自签名证书时,浏览器在启动应用程序时会发出警告。我们需要添加安全例外才能使应用程序正常工作。

创建自签名证书

首先,我们创建一个自签名证书。证书可以使用以下两种证书格式之一:PKCS12JKS

公钥加密标准 (PKCS12) 是一种受密码保护的格式,可以包含多个证书和密钥;它是一种被广泛使用的行业格式。Java KeyStore (JKS) 是一种类似于 PKCS12 的专有格式。它仅限于 Java 环境。

我们可以使用 keytool 或 OpenSSL 工具从命令行生成证书。Keytool 随 Java 运行时环境一起提供,OpenSSL 可以从 https://www.openssl.org 下载。

Keytool

Java keytool 管理一个密钥库(数据库),其中包含加密密钥、X.509 证书链和受信任证书。

$ keytool -genkeypair -alias mycert -keyalg RSA -keysize 2048 \
    -storetype PKCS12 -keystore zetcode.p12 -validity 365

使用 keytool,我们生成一组加密密钥并将它们存储在一个新的密钥库中。可以在同一个密钥库中存储多个密钥对,每个密钥对都由一个唯一的别名标识。

如果我们没有像在这种情况下那样显式指定密码,则该工具将为密钥库和一些其他选项请求一个密码。

-genkeypair 选项生成一个密钥对(一个公钥和相关的私钥)。它将公钥包装成一个 X.509 v3 自签名证书,该证书存储为一个单元素证书链。此证书链和私钥存储在由别名标识的新密钥库条目中。-alias 选项为新的密钥对指定一个名称。-keyalg 选项指定用于生成密钥对的算法,并且 keysize 值指定要生成的每个密钥的大小。

-storetype 指定存储类型,可以是 JKS 或 PKCS12。-keystore 为新存储指定一个名称。-validity 选项指定证书的有效天数。

Spring Boot HTTPS 示例

以下应用程序演示了如何使用先前创建的自签名证书设置 HTTPS。此外,我们将 HTTP 流量重定向到 HTTPS。

build.gradle
...
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───config
│   │           │       WebConfig.java
│   │           └───controller
│   │                   MyController.java
│   └───resources
│           application.properties
│           zetcode.p12
└───test
    └───java

这是项目结构。我们将密钥库数据库移动到 src/main/resources 目录。

build.gradle
plugins {
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

这是 Gradle 构建文件。这是一个非常简单的 Web 应用程序,因此我们只需要 spring-boot-starter-web 启动器。

resources/application.properties
server.port=8443
server.ssl.key-alias=mycert
server.ssl.key-store-password=s$cret
server.ssl.key-store=classpath:zetcode.p12
server.ssl.key-store-provider=SUN
server.ssl.key-store-type=PKCS12

application.properties 中,我们配置应用程序。我们指定端口、密钥对别名、密钥库密码、密钥库位置、提供程序和类型。

com/zetcode/config/WebConfig.java
package com.zetcode.config;

import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfig {

    @Bean
    public ServletWebServerFactory servletContainer() {

        var tomcat = new TomcatServletWebServerFactory() {

            @Override
            protected void postProcessContext(Context context) {

                var securityConstraint = new SecurityConstraint();
                securityConstraint.setUserConstraint("CONFIDENTIAL");

                var collection = new SecurityCollection();
                collection.addPattern("/*");
                securityConstraint.addCollection(collection);
                context.addConstraint(securityConstraint);
            }
        };

        tomcat.addAdditionalTomcatConnectors(redirectConnector());
        return tomcat;
    }

    private Connector redirectConnector() {

        var connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        connector.setPort(8080);
        connector.setSecure(false);
        connector.setRedirectPort(8443);

        return connector;
    }
}

WebConfig 中,我们配置 Tomcat(默认的 Spring Boot 嵌入式服务器)以将 HTTP 流量自动重定向到 HTTPS。

com/zetcode/controller/MyController.java
package com.zetcode.controller;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @GetMapping(value = "/hello", produces = MediaType.TEXT_PLAIN_VALUE)
    public String hello(){

        return "hello there";
    }
}

在我们的控制器中,我们有一个简单的页面,它返回一条文本消息。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application  {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application 是设置 Spring Boot 应用程序的入口点。

我们使用 ./gradlew -q bootRun 运行应用程序,并导航到 https://:8443/hello。请注意,当应用程序第一次启动时,浏览器会显示一个巨大的错误消息,提示网站证书不受信任。我们需要为我们的应用程序添加一个安全例外才能使其工作。

在本文中,我们已经展示了如何创建自签名证书,并使用此证书为 HTTPS 设置 Spring Boot 应用程序。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 Spring Boot 教程