当前位置: 首页 > news >正文

Spring Boot 动态热更新 HTTPS 证书的实现与原理

在实际生产环境中,HTTPS 证书定期更新是非常常见的需求。传统方式通常要求重启服务来加载新证书,但在一些高可用系统中,重启服务会造成连接中断和短暂不可用。本篇文章将介绍如何在 Spring Boot 项目中,实现 不重启服务的情况下热更新 SSL 证书 的完整方案。

一、原理说明

Spring Boot 默认使用 TomcatServletWebServerFactory 嵌入式 Tomcat 来启动 Web 服务。Tomcat 在初始化阶段会将 HTTPS 的证书信息加载到其 Connector 中。然而,Tomcat 实际上支持在运行时关闭并重新启动某个 Connector,因此我们可以:

1.缓存 Tomcat 实例和当前 HTTPS Connector;

2.当证书文件发生变化时,关闭旧的 Connector;

3.创建一个新的 Connector,加载新的证书;

4.添加到 Tomcat 实例中并启动。

整个过程无需重启 Spring Boot 应用,仅重建了 Tomcat 的 HTTPS 通道。

二、实现步骤

步骤 1:监听 Tomcat 实例并缓存 HTTPS Connector

我们通过监听 WebServerInitializedEvent 事件,在 Spring Boot 启动完成后获取 TomcatWebServer 实例,并缓存 HTTPS Connector 以便后续替换。

import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class DynamicSslReloader implements ApplicationListener<WebServerInitializedEvent> {private TomcatWebServer tomcatWebServer;private Connector httpsConnector;private final String keystorePath = "/your/path/to/keystore.p12";private final String keystorePassword = "changeit";@Overridepublic void onApplicationEvent(WebServerInitializedEvent event) {if (event.getWebServer() instanceof TomcatWebServer) {this.tomcatWebServer = (TomcatWebServer) event.getWebServer();// 获取 HTTPS Connector(假设只有一个)for (Connector connector : tomcatWebServer.getTomcat().getService().findConnectors()) {if ("https".equalsIgnoreCase(connector.getScheme())) {this.httpsConnector = connector;break;}}}}public void reloadSslContext() {if (tomcatWebServer == null || httpsConnector == null) {System.err.println("Tomcat 未初始化完成,无法重新加载证书");return;}try {// 暂停并移除旧 ConnectorhttpsConnector.pause();httpsConnector.stop();tomcatWebServer.getTomcat().getService().removeConnector(httpsConnector);// 创建新的 HTTPS ConnectorConnector newConnector = createHttpsConnector();tomcatWebServer.getTomcat().getService().addConnector(newConnector);newConnector.start();// 替换当前 Connector 引用this.httpsConnector = newConnector;System.out.println("SSL 证书热更新完成");} catch (Exception e) {e.printStackTrace();}}private Connector createHttpsConnector() {Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");connector.setScheme("https");connector.setPort(8443); // 请根据实际端口设置connector.setSecure(true);SSLHostConfig sslHostConfig = new SSLHostConfig();sslHostConfig.setCertificatesKeystoreFile(keystorePath);sslHostConfig.setCertificatesKeystorePassword(keystorePassword);sslHostConfig.setCertificatesKeystoreType("PKCS12");sslHostConfig.setProtocols("TLSv1.2,TLSv1.3");SSLHostConfigCertificate cert = new SSLHostConfigCertificate(sslHostConfig, SSLHostConfigCertificate.Type.UNDEFINED);sslHostConfig.addCertificate(cert);connector.addSslHostConfig(sslHostConfig);return connector;}
}

步骤 2:定时检测证书文件是否更新

使用 Spring 的定时任务,每隔一定时间检查证书文件是否有更新。如果发现文件被替换或修改,则调用 reloadSslContext 方法热更新证书。

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.io.File;@Component
public class CertMonitor {private final DynamicSslReloader sslReloader;private long lastModified = 0;private final String certPath = "/your/path/to/keystore.p12";public CertMonitor(DynamicSslReloader sslReloader) {this.sslReloader = sslReloader;}@Scheduled(fixedDelay = 60 * 1000) // 每 1 分钟检查一次public void check() {File file = new File(certPath);if (file.exists() && file.lastModified() > lastModified) {lastModified = file.lastModified();sslReloader.reloadSslContext();}}
}

步骤 3:启用定时任务调度

在 Spring Boot 启动类上添加 @EnableScheduling 注解以启用定时任务:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@EnableScheduling
public class SslHotReloadApp {public static void main(String[] args) {SpringApplication.run(SslHotReloadApp.class, args);}
}

三、注意事项

1.证书格式支持:上述代码使用 PKCS12 格式证书(.p12),也可替换为 JKS 格式;

2.权限问题:确保 Java 进程有权限访问新的证书文件;

3.只支持 Tomcat:适用于 Spring Boot 默认的 Tomcat,如果是 Jetty/Undertow,需要另行实现;

4.端口监听一致性:建议新建 Connector 的端口与旧的一致,否则可能导致监听端口冲突或失效;

5.生产环境建议双证书切换机制,例如蓝绿部署或双文件对比。

6.项目第一次部署时,还是需要一个在配置文件中配置好的ssl证书,和我们热更新的证书路径不一样,这样保证在首次部署时,不会出现证书不存在的问题,后续我们上传到我们代码中指定的路径,可以更新ssl证书,也可以封装自己的上传逻辑,直接通过项目中的上传接口手动上传

server:ssl:key-store: classpath:ssl/tomcat.pfxkey-store-password: 123456key-store-type: PKCS12#key-alias: ssl#enabled: true

四、总结

通过监听 WebServerInitializedEvent 获取 Tomcat 实例,并在运行时替换 HTTPS Connector,可以实现在 Spring Boot 中动态热更新 SSL 证书的能力。这种方式避免了系统重启,对于在线系统、微服务网关等具有重要意义。

相关文章:

  • 对夹式V型球阀:原理、优势与工业应用全解析
  • 【nginx】nginx的目录结构分析
  • 从零开始学A2A四:A2A 协议的安全性与多模态支持
  • CS144 Lab1实战记录:实现TCP重组器
  • git分支操作
  • 【SpringBoot+Vue自学笔记】001
  • Mybaits 快速入门
  • ThingsBoard3.9.1 MQTT Topic(2)
  • 数字孪生城市技术应用典型实践案例汇编(22个典型案例)(附下载)
  • nginx中的代理缓存
  • 短视频+直播带货平台搭建:电商系统源码开发的实用技术详解
  • 极狐GitLab GEO 功能介绍
  • 并查集(力扣2316)
  • 基础知识 - 结构体
  • 二分查找-LeetCode
  • Python内置函数---anext()
  • osu ai 论文笔记 DQN
  • LeetCode 第59题:螺旋矩阵Ⅱ
  • 【17】数据结构之图的遍历篇章
  • B端小程序如何突破常规,成为企业获客新利器?
  • “三个集中”之后:图说浦东新区28次撤乡并镇
  • 证券日报:降准今日正式落地,年内或还有降准空间
  • 泽连斯基:正在等待俄方确认参加会谈的代表团组成
  • 4月新增社融1.16万亿,还原地方债务置换影响后信贷增速超过8%
  • 王毅谈中拉命运共同体建设“五大工程”及落实举措
  • 安徽省委副秘书长、省委政研室主任余三元调任省社科院院长