Spring Boot 3中使用SSL捆绑包保护应用

保护传输中的数据安全是 Web 应用程序安全的一个重要方面,实现这一目标的一种有效方法是使用 SSL/TLS 证书。

保护 Spring Boot 应用程序的安全对于保护敏感数据和确保通信的完整性至关重要。

本文作为指南,阐明了 Spring Boot Security SSL 捆绑包在增强应用程序抵御潜在威胁方面的作用,并演示了如何配置 RestTemplate 以安全通信。

什么是SSL/TLS证书
SSL(安全套接字层)及其后继者 TLS(传输层安全性)是通过计算机网络提供安全通信的加密协议。SSL/TLS 证书通过加密客户端和服务器之间传输的数据,在建立客户端和服务器之间的安全连接方面发挥着至关重要的作用。SSL/TLS 确保传输中数据的机密性和完整性。

SSL证书的类型

  • 自签名证书:这些证书由服务器本身生成。虽然它们提供加密,但缺乏信任因素,因为它们没有由受信任的证书颁发机构 (CA) 签名。它们适合测试或内部使用。
  • CA 签名证书:这些证书由受信任的证书颁发机构颁发,非常适合生产环境。它们在客户端和服务器之间建立信任,确保安全连接。

什么是 SSL 捆绑包?
Spring Boot 3.1 中引入的 SSL Bundle 通过将密钥库、证书和私钥统一到单个实体中来简化 SSL/TLS 配置。 

SSL 捆绑包将所有与信任相关的组件和配置参数(包括密钥库、证书和私钥)封装到单个且易于控制的实体中。配置 SSL 捆绑包后,它可以用于一个或多个网络连接,无论它们是传入还是传出。

为什么使用 SSL 捆绑包来保证安全?

  • 加密: 加密应用程序和外部服务之间的数据通信,防止窃听。
  • 身份验证: 验证服务器和客户端的身份,最大限度地降低冒充风险。
  • 数据完整性: 确保传输的数据保持不变,防止篡改。
  • 简化的配置: 与分散的属性相比,SSL 捆绑包提供了更清晰、更易于管理的方法 server.ssl.* 。

使用 SSL 捆绑包保护 RestTemplate
以下是如何将 SSL 捆绑包与 RestTemplate 结合使用来实现安全的 REST API 调用:

1、生成SSL证书
第一步涉及生成 SSL 证书。在本文中,我们将使用自签名证书。在生产环境中,建议获取 CA 签名的证书。打开终端或命令提示符并从 Spring Boot 应用程序的项目根运行以下命令:

# Generate a self-signed certificate
keytool -genkeypair -alias myapplication -keyalg RSA -keysize 4096 -storetype PKCS12 -keystore keystore.p12 -validity 3650

上述命令使用 Javakeytool实用程序(Java 开发工具包 (JDK) 中包含的密钥和证书管理工具)生成密钥对并将其存储在 PKCS12 格式的密钥库文件中。请注意,系统会提示您输入一些附加信息,例如您的名字和姓氏、组织部门、城市或地区以及两个字母的国家/地区代码详细信息。

以下是该命令及其参数的详细说明:

  • -alias myapplication:这会将密钥对的别名设置为myapplication。别名是用于引用密钥库中的密钥对的名称。
  • -keysize 4096:这会将密钥大小设置为 4096 位。较大的密钥大小通常提供更强的安全性,但它们的计算成本也可能更高。
  • -storetype PKCS12:指定要创建的密钥库的类型。PKCS12 是存储私钥及其相应公钥证书的标准格式。
  • -keystore keystore.p12:这会将要创建的密钥库的文件名设置为keystore.p12。
  • -validity 3650:设置密钥对的有效期(以天为单位)。在本例中,它设置为 3650 天,相当于 10 年。

2、为 SSL 配置 Spring Boot
使用 Spring boot 3.1,我们可以设置 SSL Bundle 属性并应用各种证书来连接到一个或多个连接,例如 RestTemplate 或嵌入式服务器。application.properties在您的 Spring Boot 应用程序中,在或文件中配置 SSL 属性application.yml:

spring:
  ssl:
    bundle:
      jks:
        server:
          key:
            alias: "myapplication"
          keystore:
            location:
"classpath:keystore.p12"
            password:
"javacodegeeks"
            type:
"PKCS12"

application.properties
server.port=8443
server.ssl.bundle=server
spring.ssl.bundle.jks.server.key.alias=myapplication
spring.ssl.bundle.jks.server.keystore.location=classpath:keystore.p12
spring.ssl.bundle.jks.server.keystore.password=javacodegeeks
spring.ssl.bundle.jks.server.keystore.type=PKCS12

上述配置将为 Spring boot 嵌入式服务器配置基于 JKS 的证书。

让我们分解一下提供的文件中的配置application.properties:

  • server.port=8443:
    • 此配置设置嵌入式服务器(通常是 Tomcat)侦听传入 HTTPS 请求的端口。在本例中,它设置为 8443,这是安全连接的常见默认端口。
  • server.ssl.bundle=server:
    • 该属性是指示 SSL 捆绑包名称的配置。在本例中,名称设置为server。
  • spring.ssl.bundle.jks.server.key.alias=myapplication:
    • 指定梯形校正中键条目的别名。在本例中,别名设置为myapplication。
  • spring.ssl.bundle.jks.server.keystore.location=classpath:keystore.p12:
    • 指定密钥库文件的位置。在此示例中,密钥库是位于类路径上且名为 的 PKCS12 文件keystore.p12。该classpath:前缀表示密钥库与应用程序捆绑在一起。
  • spring.ssl.bundle.jks.server.keystore.password=javacodegeeks:
    • 设置访问密钥库的密码。确保此密码与用于保护密钥库文件的实际密码匹配。
  • spring.ssl.bundle.jks.server.keystore.type=PKCS12:
    • 指定密钥库的类型。在本例中,它设置为 PKCS12,表示密钥库遵循 PKCS12 格式。PKCS12 是密钥库的常用格式,尤其是在 Java 应用程序中。

要为 RestClient 配置另一个 PEM 证书,我们将使用相同的keystore.p12证书并将其转换为基于 PEM 的.crt文件.key,并将其用作 PEM 的另一个配置。

openssl pkcs12 -in keystore.p12 -nokeys -out src/main/resources/client.crt
openssl pkcs12 -in keystore.p12 -nocerts -nodes -out src/main/resources/private.key

更新application.properties文件以用于spring.ssl.bundle.pem为生成的 PEM 编码文件配置捆绑包,如下所示:

spring.ssl.bundle.pem.client.keystore.certificate=classpath:client.crt
spring.ssl.bundle.pem.client.keystore.private-key=classpath:private.key

3、创建服务
创建一个名为 ProductService 的服务,以使用配置的 RestTemplate bean 调用 ( https://jsonplaceholder.typicode.com/) 上的免费远程 API。

@Service
public class ProductService {
 
    private final RestTemplate restTemplate;
 
    @Autowired
    public ProductService(RestTemplate restTemplate) {
 
        this.restTemplate = restTemplate;
 
    }
 
    public String remoteRestCall() {
 
        return this.restTemplate.getForObject("https://jsonplaceholder.typicode.com/todos", String.class);
    }
 
}

上面的代码利用 a通过将 HTTP GET 请求发送到指定的 URL(“ https://jsonplaceholder.typicode.com/todos ”)RestTemplate来进行远程 REST 调用。

4、创建控制器
创建一个名为 的控制器ProductController。为简单起见,我们的控制器返回一个 JSON 字符串,并且还包含一个用于将 HTTP 请求的处理委托给我们的服务类 ( ProductService) 的方法。该方法remoteRestCall()从productServicebean 调用方法。

@RestController
@RequestMapping("/api")
public class ProductController {
 
    private final ProductService productService;
 
    @Autowired
    public ProductController(ProductService productService) {
        this.productService = productService;
    }
 
    @GetMapping(
"/product")
    public String product() {
       
// Construct a JSON string (for simplicity, using a hardcoded string)
        String jsonString =
"{\"name\": \"Real World Java EE Patterns\"}";
        return jsonString;
    }
 
    @GetMapping(
"/testssl")
    public String remoteRestCall() {
        return productService.remoteRestCall();
    }
      
}

接下来,让我们RestTemplate使用 aRestTemplateBuilder和 an SslBundlesbean 配置一个 bean。

@SpringBootApplication
public class SecureappWithSslApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SecureappWithSslApplication.class, args);
    }
 
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
        return restTemplateBuilder.setSslBundle(sslBundles.getBundle("server")).build();
    }
 
}

上面的代码RestTemplate使用提供的配置一个实例RestTemplateBuilder。setSslBundle()调用该方法restTemplateBuilder来应用 SSL 捆绑包,并检索从beansslBundles.getBundle("server")指定的 SSL 捆绑包。serverSslBundles

运行并测试应用程序
现在,当我们可以运行 Spring Boot 应用程序时,我们应该在终端控制台上看到以下日志消息:

main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https)
 
Connector [https-jsse-nio-8443], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/Users/username/.keystore] using alias [myapplication] with trust store [null]
 
main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8443 (https) with context path ''

这些日志消息确认应用程序已启动并使用HTTPS在端口8443上运行。现在我们可以使用curl 或打开浏览器并导航到位于 的端点URI 来发出请求。https://localhost:8443/api/product

我们可以使用curl命令向免费的远程API发出请求,并通过以下命令获取有关发生的SSL握手的一些信息:
curl --verbose --insecure https://localhost:843/api/testssl


结论
在本文中,我们演练了获取 SSL 证书、为 SSL 配置 Spring Boot 应用程序、设置 RestTemplate 以安全通信以及创建一个简单的控制器来测试安全通信的过程。

此外,本文还提供了一个完整的示例代码片段,演示application.properties如何配置 SSL 捆绑包。这包括设置服务器端口、SSL 捆绑信息和密钥库详细信息。

总之,我们可以增强 Spring Boot 3 应用程序的安全性,以保护敏感信息并为通过 HTTPS 进行安全通信奠定基础。最后要注意的是,在生产环境中使用受信任的 SSL 证书对于确保更安全和更受信任的应用程序至关重要。