我有一个后台进程,可以更新 Tomcat 用于其 SSL 凭据的密钥库。我希望能够让 Tomcat 自动重新加载它,而无需手动重启。
是否可以让 Tomcat 无需重新启动即可重新加载它,或者是否有可以通过编程的方式来完成?
答案1
尽管您的后台进程可以自动重新启动 tomcat,但我不相信有办法自动执行此操作。密钥库仅在初始化 jvm 时读取一次。如果您要编写自己的处理程序来定期重新检查密钥库,可能会有一个解决方案,但我个人在互联网上没有找到任何此类示例。
答案2
您可以重新启动单个 Tomcat 连接器,即在更改 jssecacert 文件后可以重新启动 8443 等端口。
这是我在添加/删除证书后重新启动 tomcat 连接器的完整代码/方法。
// Stop and restart the SSL connection so that the tomcat server will
// re-read the certificates from the truststore file.
public void refreshTrustStore() throws Exception
{
try
{
//following line should be replaced based on where you get your port number. You may pass in as argument to this method
String httpsPort = configurationManager.getHttpsPort();
String objectString = "*:type=Connector,port=" + httpsPort + ",*";
final ObjectName objectNameQuery = new ObjectName(objectString);
for (final MBeanServer server: MBeanServerFactory.findMBeanServer(null))
{
if (!server.queryNames(objectNameQuery, null).isEmpty())
{
MBeanServer mbeanServer = server;
ObjectName objectName = (ObjectName) server.queryNames(objectNameQuery, null).toArray()[0];
mbeanServer.invoke(objectName, "stop", null, null);
// Polling sleep to reduce delay to safe minimum.
// Use currentTimeMillis() over nanoTime() to avoid issues
// with migrating threads across sleep() calls.
long start = System.currentTimeMillis();
// Maximum of 6 seconds, 3x time required on an idle system.
long max_duration = 6000L;
long duration = 0L;
do
{
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
duration = (System.currentTimeMillis() - start);
} while (duration < max_duration &&
server.queryNames(objectNameQuery, null).size() > 0);
// Use below to get more accurate metrics.
String message = "TrustStoreManager TrustStore Stop: took " + duration + "milliseconds";
logger.information(message);
mbeanServer.invoke(objectName, "start", null, null);
break;
}
}
}
catch (Exception exception)
{
// Log and throw exception
throw exception
}
}
答案3
从 tomcat v8.5.24 开始,有一种方法可以做到这一点。
他们引入了两种方法,分别如下:
- reloadSslHostConfig(String hostName) - 重新加载特定主机
- reloadSslHostConfigs() - 重新加载所有
它们可以通过多种方式调用:
- 使用 jmx
- 使用管理器服务(在 tomcat v9.xx 中)
- 通过制定自定义协议 - 我在研究过程中发现了这种方法
在 tomcat 文档中可以轻松找到方法 1 和方法 2 的详细信息。
方法三的使用方法如下:
- 创建一个扩展你选择的协议的类,例如 Http11NioProtocol
- 覆盖所需的方法并仅在其中调用 super 以保持默认行为
- 在此类中创建一个线程,以便不时调用 reloadSslHostConfigs 方法
- 将此类打包成一个 jar 并将该 jar 放在 tomcat 的 lib 文件夹中
- 编辑 server.xml 中连接器中的协议以使用此自定义协议
查找下面的示例代码:
主要协议类:
package com.myown.connector;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.net.ssl.SSLSessionContext;
import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractJsseEndpoint;
import org.apache.tomcat.util.net.GetSslConfig;
import org.apache.tomcat.util.net.SSLContext;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.apache.tomcat.util.net.SSLImplementation;
import org.apache.tomcat.util.net.SSLUtil;
public class ReloadProtocol extends Http11NioProtocol {
private static final Log log = LogFactory.getLog(Http12ProtocolSSL.class);
public ReloadProtocol() {
super();
RefreshSslConfigThread refresher = new
RefreshSslConfigThread(this.getEndpoint(), this);
refresher.start();
}
@Override
public void setKeystorePass(String s) {
super.setKeystorePass(s);
}
@Override
public void setKeyPass(String s) {
super.setKeyPass(s);
}
@Override
public void setTruststorePass(String p) {
super.setTruststorePass(p);
}
class RefreshSslConfigThread extends Thread {
AbstractJsseEndpoint<?> abstractJsseEndpoint = null;
Http11NioProtocol protocol = null;
public RefreshSslConfigThread(AbstractJsseEndpoint<?> abstractJsseEndpoint, Http11NioProtocol protocol) {
this.abstractJsseEndpoint = abstractJsseEndpoint;
this.protocol = protocol;
}
public void run() {
int timeBetweenRefreshesInt = 1000000; // time in milli-seconds
while (true) {
try {
abstractJsseEndpoint.reloadSslHostConfigs();
System.out.println("Config Updated");
} catch (Exception e) {
System.out.println("Problem while reloading.");
}
try {
Thread.sleep(timeBetweenRefreshesInt);
} catch (InterruptedException e) {
System.out.println("Error while sleeping");
}
}
}
}
}
server.xml 中的连接器应该将此作为协议提及:
<Connector protocol="com.myown.connector.ReloadProtocol"
..........
希望这可以帮助。
答案4
2024:您现在可以启用内置TLSCertificateReloadListener
监控 TLS 证书的到期日期,并在 TLS 证书到期前一定天数触发 TLS 配置的自动重新加载。
在 tomcat 版本中可用:
- 11.0.x 适用于 11.0.0-M12 及以上版本
- 10.1.x 适用于 10.1.14 及以上版本
- 9.0.x 适用于 9.0.81 及以上版本
- 8.5.x 适用于 8.5.94 及以上版本
也可以看看错误 65770。