Apache Commons Net 共享SSLSession问题解决

    某些服务器会默认开启TLS会话恢复,如FileZilla Server 1.0及以后的版本(相对于1.0以前版本就是先当与勾选了Require TLS session resumption on data connect when using PORT P)。Apache Commons Net目前是不支持TLS会话恢复的,所以我们只能通过重写FTPSClient来实现。不然你就会经常看到报错如:425 Unable to build data connection: TLS session of data connection not resumed.

package net.evecom.iaplatform.common.ftp;

import org.apache.commons.net.ftp.FTPSClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;

/**
 * <p>
 * <B>Description: 共享SSLSession  FTPS客户端</B>
 * </P>
 *
 * @author Ryan Huang
 * @version 1.0
 */
public class SharedSSLFTPSClient extends FTPSClient {

    public SharedSSLFTPSClient(String protocol, boolean isImplicit) {
        super(protocol, isImplicit);
    }

    private static final Logger logger = LoggerFactory.getLogger(SharedSSLFTPSClient.class);

    @Override
    protected void _prepareDataSocket_(final Socket socket) throws IOException {
        if (socket instanceof SSLSocket) {
            // Control socket is SSL
            final SSLSession session = ((SSLSocket) _socket_).getSession();
            final SSLSessionContext context = session.getSessionContext();
            context.setSessionCacheSize(0); // you might want to limit the cache
            try {
                final Field sessionHostPortCache = context.getClass()
                        .getDeclaredField("sessionHostPortCache");
                sessionHostPortCache.setAccessible(true);
                final Object cache = sessionHostPortCache.get(context);
                final Method method = cache.getClass().getDeclaredMethod("put", Object.class,
                        Object.class);
                method.setAccessible(true);
                String key = String.format("%s:%s", socket.getInetAddress().getHostName(),
                        String.valueOf(socket.getPort())).toLowerCase(Locale.ENGLISH);
                method.invoke(cache, key, session);
                key = String.format("%s:%s", socket.getInetAddress().getHostAddress(),
                        String.valueOf(socket.getPort())).toLowerCase(Locale.ENGLISH);
                method.invoke(cache, key, session);
            }
            catch (NoSuchFieldException e) {
                // Not running in expected JRE
                logger.warn("No field sessionHostPortCache in SSLSessionContext", e);
            }
            catch (Exception e) {
                // Not running in expected JRE
                logger.warn(e.getMessage());
            }
        }

    }

}

注意:对于JDK1.8,使用上述方法即可解决。但是如果使用JDK11,需要在连接前指定协议ftpsClient.setEnabledProtocols(new String[]{"TLSv1.2"});。因为JDK11默认使用TLSv1.3,但是目前TLSv1.3不支持共享SSLSession.
参考地址:https://docs.spring.io/spring-integration/reference/ftp/advanced-configuration.html

posted @   IamHzc  阅读(112)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示
主题色彩