对SSL证书(在其密钥存储库中)进行任何更改后,我们需要重新启动spring引导应用程序。我希望定期(可能每年)更新我的密钥存储项,但希望避免重新启动JVM。实现这个目标需要什么?我想知道是否编写自定义KeyManager是一个可接受的做法?
不幸的是,这是不可能的。
,
你有几个解决办法。
重新加载Tomcat连接器(有点黑客)
您可以重新启动Tomcat
连接器,即在更改jssecacert
文件后重新启动8843
是可能的。
但我认为它仍然是一个hack。
反向代理:Nginx
, Apache
这是一条路。您的应用程序应该在一些反向代理(例如nginx
)后面。这将为您提供额外的灵活性,并减少应用程序的负载。Nginx
将处理https
并将其转换为普通http
。无论如何,您必须重新启动nginx
,但nginx
重新启动如此之快,以至于不会停机。此外,您可以配置脚本为您做这些。
在Tomcat上可以使用本地JMX重新加载SSL上下文:
private static final String JMX_THREAD_POOL_NAME = "*:type=ThreadPool,name=*";
private static final String JMX_OPERATION_RELOAD_SSL_HOST_CONFIGS_NAME = "reloadSslHostConfigs";
private void reloadSSLConfigsOnConnectors() {
try {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName(JMX_THREAD_POOL_NAME);
Set<ObjectInstance> allTP = server.queryMBeans(objectName, null);
logger.info("MBeans found: {}", allTP.size());
allTP.forEach(tp -> reloadSSLConfigOnThreadPoolJMX(server, tp));
} catch (Exception ex) {
logger.error("", ex);
}
}
private void reloadSSLConfigOnThreadPoolJMX(MBeanServer server, ObjectInstance tp) {
try {
logger.info("Invoking operation SSL reload on {}", tp.getObjectName());
server.invoke(tp.getObjectName(), JMX_OPERATION_RELOAD_SSL_HOST_CONFIGS_NAME, new Object[]{}, new String[]{});
logger.trace("Successfully invoked");
} catch (Exception ex) {
logger.error("Invoking SSL reload", ex);
}
}
我重新加载所有ThreadPool SSL上下文,但你真的只需要一个:Tomcat:type=ThreadPool,name=https-jsse-nio-8443
。我只是担心名字会变,所以我把所有的可能性都写了,以防万一。
我已经在我的Spring Boot应用程序中解决了这个问题TomcatServletWebServerFactory
在bean中添加我自己的连接器定制器。
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
// --- CUSTOMIZE SSL PORT IN ORDER TO BE ABLE TO RELOAD THE SSL HOST CONFIG
tomcat.addConnectorCustomizers(new DefaultSSLConnectorCustomizer());
return tomcat;
}
我的定制器为https提取协议以供以后使用
public class DefaultSSLConnectorCustomizer implements TomcatConnectorCustomizer {
private Http11NioProtocol protocol;
@Override
public void customize(Connector connector) {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
if ( connector.getSecure()) {
//--- REMEMBER PROTOCOL WHICH WE NEED LATER IN ORDER TO RELOAD SSL CONFIG
this.protocol = protocol;
}
}
protected Http11NioProtocol getProtocol() {
return protocol;
}
}
当我用新的私钥更新密钥存储库时,我执行SSL主机配置重新加载。这段代码在这里
@Component
public class TomcatUtil {
public static final String DEFAULT_SSL_HOSTNAME_CONFIG_NAME = "_default_";
private final Logger logger = LoggerFactory.getLogger(getClass());
private ServletWebServerFactory servletWebServerFactory;
public TomcatUtil(ServletWebServerFactory servletWebServerFactory) {
this.servletWebServerFactory = servletWebServerFactory;
}
public void reloadSSLHostConfig() {
TomcatServletWebServerFactory tomcatFactoty = (TomcatServletWebServerFactory) servletWebServerFactory;
Collection<TomcatConnectorCustomizer> customizers = tomcatFactoty.getTomcatConnectorCustomizers();
for (TomcatConnectorCustomizer tomcatConnectorCustomizer : customizers) {
if (tomcatConnectorCustomizer instanceof DefaultSSLConnectorCustomizer) {
DefaultSSLConnectorCustomizer customizer = (DefaultSSLConnectorCustomizer) tomcatConnectorCustomizer;
Http11NioProtocol protocol = customizer.getProtocol();
try {
protocol.reloadSslHostConfig(DEFAULT_SSL_HOSTNAME_CONFIG_NAME);
logger.info("Reloaded SSL host configuration");
} catch (IllegalArgumentException e) {
logger.warn("Cannot reload SSL host configuration", e);
}
}
}
}
}
最后和
...
renewServerCertificate();
tomcatUtil.reloadSSLHostConfig();