You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置Tomcat 9,使应用代码可获取客户端证书并自行验证?

Tomcat9 实现仅REST API的客户端证书自定义认证

问题分析

你当前配置clientAuth="want"并指定truststoreFile时,Tomcat会自动验证客户端证书是否在信任库中,无效证书会被直接拦截拒绝,不符合你“让应用代码自主验证”的需求。要实现仅REST API路径的自定义证书验证,需要调整Tomcat配置+应用内Filter拦截的组合方案。

解决方案步骤

1. 修改Tomcat Connector配置

调整server.xml中的SSL Connector,移除truststore相关配置,让Tomcat仅请求客户端证书但不验证有效性,将证书传递给应用处理:

<Connector            
    protocol="org.apache.coyote.http11.Http11NioProtocol"              
    port="8443" 
    maxThreads="200"
    scheme="https"
    secure="true"
    SSLEnabled="true"
    keyAlias="tthserver" 
    keystoreFile="C:\Temp\keystore\keystore.jks" 
    keystorePass="PW"
    clientAuth="want" 
    sslProtocol="TLS" 
/>
  • clientAuth="want":Tomcat会主动请求客户端证书,但不强制要求,无证书的请求也能正常到达应用
  • 移除truststoreFile和trustStorePass:避免Tomcat自动验证证书有效性,仅将证书信息传递给应用

2. 应用内添加自定义认证Filter

创建一个Filter,仅拦截REST API路径(比如/api/**),在Filter中获取客户端证书并执行自定义验证逻辑:

编写Filter代码

public class ClientCertAuthFilter implements Filter {
    private X509TrustManager trustManager;

    @Override
    public void init(FilterConfig config) throws ServletException {
        // 加载自定义信任库,用于验证客户端证书
        try {
            KeyStore trustStore = KeyStore.getInstance("JKS");
            trustStore.load(new FileInputStream("C:\\Temp\\keystore\\truststore.jks"), "PW".toCharArray());
            
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(trustStore);
            
            TrustManager[] trustManagers = tmf.getTrustManagers();
            this.trustManager = (X509TrustManager) trustManagers[0];
        } catch (Exception e) {
            throw new ServletException("初始化信任库失败", e);
        }
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        // 仅对REST API路径执行证书验证
        if (request.getRequestURI().startsWith("/api/")) {
            X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
            
            // 无证书的情况
            if (certs == null || certs.length == 0) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "需要提供客户端证书");
                return;
            }

            X509Certificate clientCert = certs[0];
            try {
                // 验证证书是否在信任库中
                trustManager.checkClientTrusted(new X509Certificate[]{clientCert}, "RSA");
                // 可选:添加额外验证逻辑,比如检查证书有效期、主题DN等
                if (clientCert.getNotAfter().before(new Date())) {
                    throw new CertificateException("证书已过期");
                }
            } catch (CertificateException e) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效的客户端证书:" + e.getMessage());
                return;
            }
        }

        // 验证通过,继续执行后续请求
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {
        // 资源清理
    }
}

配置Filter到web.xml

在应用的web.xml中注册Filter,并指定拦截路径:

<filter>
    <filter-name>ClientCertAuthFilter</filter-name>
    <filter-class>com.your.package.ClientCertAuthFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ClientCertAuthFilter</filter-name>
    <url-pattern>/api/*</url-pattern>
</filter-mapping>

关键说明

  • 此方案中,Tomcat仅负责传递证书信息,所有验证逻辑由应用代码控制,完全满足你“仅REST API验证、其他请求不受影响”的需求
  • 无证书或无效证书的请求不会被Tomcat拦截,而是由Filter返回401错误,你也可以根据业务需求调整返回逻辑
  • 建议将信任库路径、密码等配置提取到环境变量或配置文件中,避免硬编码

内容的提问来源于stack exchange,提问作者Susanne Tietz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 01:35:25