You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Java 17 Spring Boot集成Jetty的mTLS微服务自定义SSL握手失败日志实现求助

Spring Boot 17 + Jetty mTLS 自定义SSL握手失败日志实现

以下是完整的代码实现步骤,涵盖Jetty容器配置、mTLS启用以及自定义握手失败处理器:

1. 调整依赖(排除Tomcat,引入Jetty)

pom.xml中替换默认的Tomcat容器为Jetty:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-ssl</artifactId>
    </dependency>
</dependencies>

2. 自定义SSL握手失败处理器

实现Jetty的SslHandshakeListener接口,根据异常类型分类处理握手失败事件,替换为你需要的自定义日志/错误处理逻辑:

import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CustomSslHandshakeFailureHandler implements SslContextFactory.SslHandshakeListener {

    private static final Logger CUSTOM_LOGGER = Logger.getLogger(CustomSslHandshakeFailureHandler.class.getName());

    @Override
    public void handshakeStarted(EndPoint endPoint, SSLSession sslSession) {
        // 可选:记录握手开始事件
        CUSTOM_LOGGER.info("SSL握手开始,客户端地址:" + endPoint.getRemoteAddress());
    }

    @Override
    public void handshakeSucceeded(EndPoint endPoint, SSLSession sslSession) {
        // 可选:记录握手成功事件
        try {
            String peerPrincipal = sslSession.getPeerPrincipal().getName();
            CUSTOM_LOGGER.info("SSL握手成功,客户端证书主体:" + peerPrincipal);
        } catch (Exception e) {
            CUSTOM_LOGGER.warning("无法获取客户端证书主体信息");
        }
    }

    @Override
    public void handshakeFailed(EndPoint endPoint, SSLSession sslSession, Throwable failure) {
        String clientAddr = endPoint.getRemoteAddress().toString();
        String errorMsg;

        if (failure instanceof SSLHandshakeException) {
            String errMsg = failure.getMessage();
            if (errMsg.contains("unable to find valid certification path")) {
                errorMsg = "客户端证书未被信任CA识别";
            } else if (errMsg.contains("no cipher suites in common")) {
                errorMsg = "密钥套件不匹配";
            } else if (errMsg.contains("Bad certificate")) {
                errorMsg = "客户端证书无效/过期";
            } else {
                errorMsg = "SSL握手失败:" + errMsg;
            }
        } else {
            errorMsg = "SSL握手异常:" + failure.getClass().getSimpleName() + " - " + failure.getMessage();
        }

        // 这里可以替换为自定义逻辑:写入数据库、发送告警、存储到自定义日志系统等
        CUSTOM_LOGGER.log(Level.SEVERE, "客户端[" + clientAddr + "] " + errorMsg, failure);
    }
}

3. 配置Jetty mTLS与自定义处理器

创建Spring配置类,自定义Jetty服务器的SSL连接工厂,启用mTLS并添加自定义握手监听器:

import org.eclipse.jetty.server.*;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JettyMtlsConfiguration {

    @Bean
    public WebServerFactoryCustomizer<JettyServletWebServerFactory> jettyMtlsCustomizer() {
        return factory -> {
            factory.addServerCustomizers(server -> {
                // 配置SSL上下文工厂,启用mTLS
                SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
                
                // 读取配置文件中的SSL参数(也可直接硬编码,建议用配置文件)
                sslContextFactory.setKeyStorePath("classpath:server-keystore.jks");
                sslContextFactory.setKeyStorePassword("server-keystore-pass");
                sslContextFactory.setKeyManagerPassword("server-key-pass");
                sslContextFactory.setTrustStorePath("classpath:truststore.jks");
                sslContextFactory.setTrustStorePassword("truststore-pass");
                // 强制要求客户端提供证书(mTLS模式)
                sslContextFactory.setNeedClientAuth(true);
                
                // 添加自定义握手失败监听器
                sslContextFactory.addSslHandshakeListener(new CustomSslHandshakeFailureHandler());

                // 创建SSL连接工厂与HTTP连接工厂
                SslConnectionFactory sslConnFactory = new SslConnectionFactory(sslContextFactory, "http/1.1");
                HttpConnectionFactory httpConnFactory = new HttpConnectionFactory(new HttpConfiguration());

                // 配置HTTPS连接器
                ServerConnector httpsConnector = new ServerConnector(server, sslConnFactory, httpConnFactory);
                httpsConnector.setPort(8443);
                
                // 替换默认连接器
                server.setConnectors(new Connector[]{httpsConnector});
            });
        };
    }
}

4. 配置文件补充(application.yml)

将SSL参数配置到application.yml中,方便维护:

server:
  port: 8443
  ssl:
    key-store: classpath:server-keystore.jks
    key-store-password: server-keystore-pass
    key-password: server-key-pass
    trust-store: classpath:truststore.jks
    trust-store-password: truststore-pass
    client-auth: need

5. 证书准备说明

  • server-keystore.jks:服务端的证书密钥库,包含服务端SSL证书与私钥
  • truststore.jks:信任库,包含用于验证客户端证书的CA根证书或中间证书
  • 可以用keytool命令生成或导入证书:
    # 生成服务端密钥库
    keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -storetype JKS -keystore server-keystore.jks -validity 3650
    # 导入CA证书到信任库
    keytool -importcert -alias ca-cert -file ca.crt -storetype JKS -keystore truststore.jks
    

验证测试

使用curl发送无效请求,验证自定义处理器是否记录对应错误:

# 无证书请求(触发"客户端证书未提供"相关错误)
curl -v https://localhost:8443/api/test
# 使用无效证书请求
curl -v --cert invalid-client.crt --key invalid-client.key --cacert ca.crt https://localhost:8443/api/test

内容的提问来源于stack exchange,提问作者Colin Schofield

火山引擎 最新活动