Loading

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 @ 2024-10-30 21:09  IamHzc  阅读(39)  评论(0编辑  收藏  举报