Spring Webflux:第二次请求时相互身份验证失败

q5iwbnjs  于 2023-06-28  发布在  Spring
关注(0)|答案(1)|浏览(185)

我在TLS相互身份验证(服务器证书和客户端证书)的上下文中。
我的客户端是一个带有webflux(WebClient)的Sping Boot 应用程序。WebClient示例是一次性创建的(客户端作为服务24/24小时运行)。
我的服务器也是一个配置为相互身份验证的Sping Boot 应用程序。
第一个请求就像一个魅力。
第二个请求(大约10分钟后)总是失败,客户端出现以下错误:

javax.net.ssl.SSLException: Received fatal alert: internal_error
        at sun.security.ssl.Alert.createSSLException(Alert.java:133) ~[?:?]
        at sun.security.ssl.Alert.createSSLException(Alert.java:117) ~[?:?]
        at sun.security.ssl.TransportContext.fatal(TransportContext.java:356) ~[?:?]
        at sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293) ~[?:?]
        at sun.security.ssl.TransportContext.dispatch(TransportContext.java:202) ~[?:?]
        at sun.security.ssl.SSLTransport.decode(SSLTransport.java:171) ~[?:?]
        at sun.security.ssl.SSLEngineImpl.decode(SSLEngineImpl.java:736) ~[?:?]
        at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:691) ~[?:?]
        at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:506) ~[?:?]
        at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:482) ~[?:?]
        at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:679) ~[?:?]
        at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:297) ~[netty-handler-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1352) ~[netty-handler-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1245) ~[netty-handler-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1294) ~[netty-handler-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529) ~[netty-codec-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468) ~[netty-codec-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) ~[netty-codec-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800) ~[netty-transport-classes-epoll-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499) ~[netty-transport-classes-epoll-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397) ~[netty-transport-classes-epoll-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.89.Final.jar!/:4.1.89.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.89.Final.jar!/:4.1.89.Final]
        at java.lang.Thread.run(Thread.java:831) [?:?]

...以及服务器端的以下错误:

org.apache.tomcat.util.net.TLSClientHelloExtractor The ClientHello was not presented in a single TLS record so no SNI information could be extracted

我假设服务器期望客户端再次进行身份验证,而客户端认为没有必要再次进行身份验证。
我是否需要进一步配置我的WebClient以保持连接打开或类似的事情?
我可以为每个请求重新创建WebClient,但我认为没有必要使用正确的配置。
谢谢你帮忙

06odsfpq

06odsfpq1#

我最终解决了这个问题。
这是WebClient示例化的错误代码:

var keyStoreInputStream = keyStoreResource.getInputStream();
var keyStore = readKeyStore(keyStoreType, keyStoreInputStream, keyStorePassword);
var keyManagerFactory = KeyManagerFactory.getInstance(getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword);

var trustStoreInputStream = trustStoreResource.getInputStream();
var trustStore = readKeyStore(trustStoreType, trustStoreInputStream, trustStorePassword);
var trustManagerFactory = TrustManagerFactory.getInstance(getDefaultAlgorithm());
trustManagerFactory.init(trustStore);

var sslContext = forClient().
        keyManager(keyManagerFactory).
        trustManager(trustManagerFactory).
        build();

var httpClient = HttpClient.create().
        secure(ssl -> ssl.sslContext(sslContext));

var clientConnector = new ReactorClientHttpConnector(httpClient);

var webClient = WebClient.builder().
        clientConnector(clientConnector).
        baseUrl(baseUrl).
        build();

使用此配置,底层通道在60秒不活动(无请求)后关闭。我不知道这是由于客户端或服务器配置。
通道关闭后,如果我执行另一个请求,它会自动打开一个新通道,但会重用相同的SSL会话。这就是客户端不执行新的SSL握手的原因。这就是为什么我在服务器端得到以下错误:

org.apache.tomcat.util.net.TLSClientHelloExtractor The ClientHello was not presented in a single TLS record so no SNI information could be extracted

我解决这个问题的想法是让SSL会话在通道使用这个额外的SSL上下文配置之前过期:

var sslContext = forClient().
        keyManager(keyManagerFactory).
        trustManager(trustManagerFactory).
        sessionTimeout(30).
        build();

它碰巧起作用了,SSL会话在通道超时之前超时,如果需要,SSL握手会再次执行。
我认为这不是一个非常干净的解决方案,我仍然愿意接受建议,但我认为这个问题已经解决了。

相关问题