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

Spring Boot Rest控制器中如何获取SSLSession内的TLS指纹数据?

实现步骤

1. 自定义SSLEngine包装类,捕获握手事件并存储TLS指纹

创建包装类封装原生SSLEngine,添加握手完成监听器,在握手结束后生成TLS指纹并存入SSLSession属性:

import javax.net.ssl.*;
import java.nio.ByteBuffer;

public class FingerprintSSLEngine extends SSLEngine {
    private final SSLEngine delegate;

    public FingerprintSSLEngine(SSLEngine delegate) {
        this.delegate = delegate;
        delegate.addHandshakeCompletedListener(this::onHandshakeCompleted);
    }

    private void onHandshakeCompleted(HandshakeCompletedEvent event) {
        SSLSession session = event.getSession();
        try {
            String tlsFingerprint = generateTlsFingerprint(session);
            session.putValue("TLS_FINGERPRINT", tlsFingerprint);
        } catch (Exception e) {
            // 生产环境建议替换为日志记录
            e.printStackTrace();
        }
    }

    private String generateTlsFingerprint(SSLSession session) {
        // 示例:基于协议版本+加密套件生成简易指纹,生产环境请用标准JA3/HA2等指纹算法
        String protocol = session.getProtocol();
        String cipherSuite = session.getCipherSuite();
        return String.format("%s_%s", protocol, cipherSuite).hashCode() + "";
    }

    // 重写所有SSLEngine方法,委托给原生实例
    @Override
    public void setUseClientMode(boolean mode) {
        delegate.setUseClientMode(mode);
    }

    @Override
    public boolean getUseClientMode() {
        return delegate.getUseClientMode();
    }

    @Override
    public void setNeedClientAuth(boolean need) {
        delegate.setNeedClientAuth(need);
    }

    @Override
    public boolean getNeedClientAuth() {
        return delegate.getNeedClientAuth();
    }

    @Override
    public void setWantClientAuth(boolean want) {
        delegate.setWantClientAuth(want);
    }

    @Override
    public boolean getWantClientAuth() {
        return delegate.getWantClientAuth();
    }

    @Override
    public void setEnableSessionCreation(boolean flag) {
        delegate.setEnableSessionCreation(flag);
    }

    @Override
    public boolean getEnableSessionCreation() {
        return delegate.getEnableSessionCreation();
    }

    @Override
    public String getPeerHost() {
        return delegate.getPeerHost();
    }

    @Override
    public int getPeerPort() {
        return delegate.getPeerPort();
    }

    @Override
    public SSLSession getSession() {
        return delegate.getSession();
    }

    @Override
    public void beginHandshake() throws SSLException {
        delegate.beginHandshake();
    }

    @Override
    public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) throws SSLException {
        return delegate.wrap(srcs, offset, length, dst);
    }

    @Override
    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) throws SSLException {
        return delegate.unwrap(src, dsts, offset, length);
    }

    @Override
    public void closeInbound() throws SSLException {
        delegate.closeInbound();
    }

    @Override
    public boolean isInboundDone() {
        return delegate.isInboundDone();
    }

    @Override
    public void closeOutbound() {
        delegate.closeOutbound();
    }

    @Override
    public boolean isOutboundDone() {
        return delegate.isOutboundDone();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public String[] getEnabledCipherSuites() {
        return delegate.getEnabledCipherSuites();
    }

    @Override
    public void setEnabledCipherSuites(String[] suites) {
        delegate.setEnabledCipherSuites(suites);
    }

    @Override
    public String[] getSupportedProtocols() {
        return delegate.getSupportedProtocols();
    }

    @Override
    public String[] getEnabledProtocols() {
        return delegate.getEnabledProtocols();
    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        delegate.setEnabledProtocols(protocols);
    }

    @Override
    public SSLSessionContext getSessionContext() {
        return delegate.getSessionContext();
    }

    @Override
    public void setHandshakeApplicationProtocolSelector(HandshakeApplicationProtocolSelector selector) {
        delegate.setHandshakeApplicationProtocolSelector(selector);
    }

    @Override
    public HandshakeApplicationProtocolSelector getHandshakeApplicationProtocolSelector() {
        return delegate.getHandshakeApplicationProtocolSelector();
    }

    @Override
    public String getApplicationProtocol() {
        return delegate.getApplicationProtocol();
    }

    @Override
    public String getNegotiatedApplicationProtocol() {
        return delegate.getNegotiatedApplicationProtocol();
    }
}

2. 配置Spring Boot使用自定义SSLEngine

针对默认Tomcat容器,自定义连接器配置替换原生SSLEngine:

import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLEngine;

@Configuration
public class TomcatSSLConfig {

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> sslEngineCustomizer() {
        return factory -> factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
            AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) connector.getProtocolHandler();
            protocol.setSSLEngineImplementation(new CustomSSLEngineImpl());
        });
    }

    private static class CustomSSLEngineImpl extends org.apache.tomcat.util.net.jsse.JSSEImplementation {
        @Override
        public SSLEngine createSSLEngine() {
            return new FingerprintSSLEngine(super.createSSLEngine());
        }

        @Override
        public SSLEngine createSSLEngine(String host, int port) {
            return new FingerprintSSLEngine(super.createSSLEngine(host, port));
        }
    }
}

3. 在Rest控制器中获取TLS指纹

通过HttpServletRequest获取当前请求的SSLSession,取出存储的指纹:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.net.ssl.SSLSession;

@RestController
public class FingerprintController {

    @GetMapping("/tls-fingerprint")
    public String getTlsFingerprint(HttpServletRequest request) {
        SSLSession sslSession = (SSLSession) request.getAttribute("javax.servlet.request.ssl_session");
        if (sslSession == null) {
            return "未检测到SSL会话";
        }
        String fingerprint = (String) sslSession.getValue("TLS_FINGERPRINT");
        return "TLS指纹: " + (fingerprint != null ? fingerprint : "未生成");
    }
}

注意事项

  • 生产环境需替换示例中的generateTlsFingerprint方法为标准指纹算法(如JA3),可使用成熟第三方库实现
  • 若使用Netty/WebFlux容器,需自定义SslContext来注入自定义SSLEngine,逻辑类似但实现细节不同
  • 确保Spring Boot SSL配置正确(如server.ssl.key-store、server.ssl.key-store-password等),否则SSLEngine无法正常初始化

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:44:55