我们可以避免重启spring应用程序,刷新与其嵌入式tomcat容器相关的证书吗?

时间:2016-09-16 08:59:32

标签: java spring security ssl spring-boot

对SSL证书进行任何更改(在其密钥库中),我们需要重新启动spring boot应用程序。我想定期(可能是每年)更新我的密钥库条目,但希望避免重新启动JVM。要实现它需要什么。我想知道编写自定义KeyManager是否可以接受?

3 个答案:

答案 0 :(得分:3)

不幸的是,这是不可能的。

BUT

这里有几个解决方案。

重新加载Tomcat连接器(有点hacky)

您可以重新启动Tomcat连接器,即更改8843文件后可以重新启动jssecacert

但我认为它仍然是一个黑客。

反向代理:NginxApache

这是一种方法。您的应用程序应位于某个反向代理(例如nginx)之后。这将为您提供额外的灵活性并减少应用程序的负担。 Nginx会处理https并将其翻译为普通http。无论如何,您必须重新启动nginx,但nginx重新启动速度非常快,以至于不会停机。此外,您可以配置脚本来为您执行此操作。

答案 1 :(得分:0)

在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。我只是担心名称会更改,所以我会介绍所有可能性,以防万一。

答案 2 :(得分:0)

我已经通过在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();