文章目录
1. JSCH和SFTP基础概念
1.1 什么是JSCH?
JSCH(Java Secure Channel)是一个纯Java实现的SSH2协议库,由JCraft开发。它提供了完整的SSH客户端功能,包括:
- SFTP(SSH File Transfer Protocol):安全文件传输协议
- SCP(Secure Copy Protocol):安全复制协议
- SSH执行远程命令:在远程服务器执行命令
- 端口转发:本地和远程端口转发
- 公钥认证:支持各种公钥认证方式
1.2 SFTP协议特点
SFTP(SSH File Transfer Protocol)是基于SSH协议的文件传输协议,具有以下特点:
- 安全性高:所有数据传输都经过加密
- 跨平台:支持不同操作系统之间的文件传输
- 功能丰富:支持文件上传、下载、删除、重命名等操作
- 可靠性强:支持断点续传和完整性校验
1.3 JSCH的优势
- 纯Java实现:无需额外的本地库依赖
- 轻量级:jar包大小约280KB
- 功能完整:支持SSH2协议的大部分功能
- 开源免费:基于BSD许可证
- 活跃维护:持续更新和维护
1.4 常用场景
// 常见的SFTP使用场景示例
public class SftpUseCases {
// 1. 文件备份
public void backupFiles() {
// 将本地文件备份到远程服务器
}
// 2. 日志收集
public void collectLogs() {
// 从远程服务器下载日志文件
}
// 3. 数据同步
public void syncData() {
// 在不同服务器间同步数据文件
}
// 4. 部署文件
public void deployApplication() {
// 上传应用程序文件到服务器
}
// 5. 监控文件变化
public void monitorFileChanges() {
// 定期检查远程目录的文件变化
}
}
2. 环境配置和依赖管理
2.1 Maven依赖配置
在pom.xml
文件中添加JSCH依赖:
<dependencies>
<!-- JSCH核心依赖 -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<!-- 日志依赖(可选) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
</dependency>
<!-- Apache Commons IO(文件操作辅助) -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- JSON处理(配置文件处理) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>
2.2 Gradle依赖配置
在build.gradle
文件中添加依赖:
dependencies {
// JSCH核心依赖
implementation 'com.jcraft:jsch:0.1.55'
// 日志依赖
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'ch.qos.logback:logback-classic:1.2.12'
// 工具类依赖
implementation 'commons-io:commons-io:2.11.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
}
2.3 基础配置类
创建SFTP连接配置类:
package com.example.sftp.config;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* SFTP连接配置类
*/
public class SftpConfig {
@JsonProperty("host")
private String host;
@JsonProperty("port")
private int port = 22;
@JsonProperty("username")
private String username;
@JsonProperty("password")
private String password;
@JsonProperty("privateKeyPath")
private String privateKeyPath;
@JsonProperty("passphrase")
private String passphrase;
@JsonProperty("timeout")
private int timeout = 30000; // 30秒
@JsonProperty("strictHostKeyChecking")
private boolean strictHostKeyChecking = false;
@JsonProperty("preferredAuthentications")
private String preferredAuthentications = "publickey,password";
// 构造函数
public SftpConfig() {
}
public SftpConfig(String host, String username, String password) {
this.host = host;
this.username = username;
this.password = password;
}
public SftpConfig(String host, String username, String privateKeyPath, String passphrase) {
this.host = host;
this.username = username;
this.privateKeyPath = privateKeyPath;
this.passphrase = passphrase;
}
// Getters and Setters
public String getHost() {
return host; }
public void setHost(String host) {
this.host = host; }
public int getPort() {
return port; }
public void setPort(int port) {
this.port = port; }
public String getUsername() {
return username; }
public void setUsername(String username) {
this.username = username; }
public String getPassword() {
return password; }
public void setPassword(String password) {
this.password = password; }
public String getPrivateKeyPath() {
return privateKeyPath; }
public void setPrivateKeyPath(String privateKeyPath) {
this.privateKeyPath = privateKeyPath; }
public String getPassphrase() {
return passphrase; }
public void setPassphrase(String passphrase) {
this.passphrase = passphrase; }
public int getTimeout() {
return timeout; }
public void setTimeout(int timeout) {
this.timeout = timeout; }
public boolean isStrictHostKeyChecking() {
return strictHostKeyChecking; }
public void setStrictHostKeyChecking(boolean strictHostKeyChecking) {
this.strictHostKeyChecking = strictHostKeyChecking;
}
public String getPreferredAuthentications() {
return preferredAuthentications; }
public void setPreferredAuthentications(String preferredAuthentications) {
this.preferredAuthentications = preferredAuthentications;
}
@Override
public String toString() {
return String.format("SftpConfig{host='%s', port=%d, username='%s', timeout=%d}",
host, port, username, timeout);
}
}
2.4 配置文件示例
创建配置文件sftp-config.json
:
{
"host": "192.168.1.100",
"port": 22,
"username": "ftpuser",
"password": "password123",
"timeout": 30000,
"strictHostKeyChecking": false,
"preferredAuthentications": "password,publickey"
}
创建公钥认证配置文件sftp-config-key.json
:
{
"host": "192.168.1.100",
"port": 22,
"username": "ftpuser",
"privateKeyPath": "/path/to/private/key",
"passphrase": "keypassphrase",
"timeout": 30000,
"strictHostKeyChecking": false,
"preferredAuthentications": "publickey,password"
}
3. SFTP连接管理
3.1 基础连接类
创建SFTP连接管理类:
package com.example.sftp.core;
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
/**
* SFTP连接管理器
*/
public class SftpConnectionManager {
private static final Logger logger = LoggerFactory.getLogger(SftpConnectionManager.class);
private JSch jsch;
private Session session;
private ChannelSftp channelSftp;
private SftpConfig config;
public SftpConnectionManager(SftpConfig config) {
this.config = config;
this.jsch = new JSch();
}
/**
* 建立SFTP连接
*/
public void connect() throws JSchException {
logger.info("正在连接SFTP服务器: {}", config.getHost());
try {
// 创建会话
session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
// 设置密码认证
if (config.getPassword() != null && !config.getPassword().isEmpty()) {
session.setPassword(config.getPassword());
}
// 设置私钥认证
if (config.getPrivateKeyPath() != null && !config.getPrivateKeyPath().isEmpty()) {
if (config.getPassphrase() != null && !config.getPassphrase().isEmpty()) {
jsch.addIdentity(config.getPrivateKeyPath(), config.getPassphrase());
} else {
jsch.addIdentity(config.getPrivateKeyPath());
}
}
// 配置会话属性
Properties properties = new Properties();
properties.put("StrictHostKeyChecking",
config.isStrictHostKeyChecking() ? "yes" : "no");
properties.put("PreferredAuthentications", config.getPreferredAuthentications());
session.setConfig(properties);
// 设置超时时间
session.setTimeout(config.getTimeout());
// 建立连接
session.connect();
logger.info("成功连接到SFTP服务器");
// 打开SFTP通道
Channel channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
logger.info("SFTP通道已建立");
} catch (JSchException e) {
logger.error("连接SFTP服务器失败: {}", e.getMessage());
disconnect(); // 清理资源
throw e;
}
}
/**
* 断开SFTP连接
*/
public void disconnect() {
if (channelSftp != null && channelSftp.isConnected()) {
channelSftp.disconnect();
logger.info("SFTP通道已断开");
}
if (session != null && session.isConnected()) {
session.disconnect();
logger.info("SSH会话已断开");
}
}
/**
* 检查连接状态
*/
public boolean isConnected() {
return session != null && session.isConnected() &&
channelSftp != null && channelSftp.isConnected();
}
/**
* 重新连接
*/
public void reconnect() throws JSchException {
logger.info("正在重新连接SFTP服务器...");
disconnect();
connect();
}
/**
* 获取SFTP通道
*/
public ChannelSftp getChannelSftp() {
if (!isConnected()) {
throw new IllegalStateException("SFTP连接未建立");
}
return channelSftp;
}
/**
* 获取会话信息
*/
public String getSessionInfo() {
if (session != null) {
return String.format("连接到 %s@%s:%d, 会话状态: %s",
session.getUserName(),
session.getHost(),
session.getPort(),
session.isConnected() ? "已连接" : "未连接");
}
return "无会话信息";
}
}
3.2 连接池管理
创建SFTP连接池:
package com.example.sftp.pool;
import com.example.sftp.core.SftpConnectionManager;
import com.example.sftp.config.SftpConfig;
import com.jcraft.jsch.JSchException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* SFTP连接池
*/
public class SftpConnectionPool {
private static final Logger logger = LoggerFactory.getLogger(SftpConnectionPool.class);
private final BlockingQueue<SftpConnectionManager> connectionPool;
private final SftpConfig config;
private final int maxConnections;
private final AtomicInteger currentConnections = new AtomicInteger(0);
private volatile boolean isShutdown = false;
public SftpConnectionPool(SftpConfig config, int maxConnections) {
this.config = config;
this.maxConnections = maxConnections;
this.connectionPool = new ArrayBlockingQueue<>(maxConnections);
// 预创建一些连接
initializePool();
}
/**
* 初始化连接池
*/
private void initializePool() {
int initialConnections = Math.min(2, maxConnections);
for (int i = 0; i < initialConnections; i++) {
try {
SftpConnectionManager connection = createConnection();
connectionPool.offer(connection);
currentConnections.incrementAndGet();
logger.info("预创建SFTP连接 {}/{}", i + 1, initialConnections);
} catch (Exception e) {
logger.warn("预创建SFTP连接失败: {}", e.getMessage());
}
}
logger.info("SFTP连接池初始化完成,当前连接数: {}", currentConnections.get());
}
/**
* 创建新连接
*/
private SftpConnectionManager createConnection() throws JSchException {
SftpConnectionManager connection = new SftpConnectionManager(config);
connection.connect();
return connection;
}
/**
* 获取连接
*/
public SftpConnectionManager getConnection() throws JSchException, InterruptedException {
return getConnection(30, TimeUnit.SECONDS);
}
/**
* 获取连接(带超时)
*/
public SftpConnectionManager getConnection(long timeout, TimeUnit unit)
throws JSchException, InterruptedException {
if (isShutdown) {
throw new IllegalStateException("连接池已关闭");
}
// 先尝试从池中获取
SftpConnectionManager connection = connectionPool.poll();
if (connection != null) {
// 检查连接是否有效
if (connection.isConnected()) {
logger.debug("从连接池获取连接");
return connection;
} else {
// 连接失效,尝试重连
try {
connection.reconnect();
logger.debug("重新连接成功");
return connection;
} catch (JSchException e) {
logger.warn("重新连接失败,创建新连接");
currentConnections.decrementAndGet();
}
}
}
// 如果池中没有可用连接且未达到最大连接数,创建新连接
if (currentConnections.get() < maxConnections) {
try {
connection = createConnection();
currentConnections.incrementAndGet();
logger.debug("创建新的SFTP连接");
return connection;
} catch (JSchException e) {
logger.error("创建新连接失败: {}", e.getMessage());
throw e;
}
}
// 等待可用连接
connection = connectionPool.poll(timeout, unit);
if (connection == null) {
throw new RuntimeException("获取SFTP连接超时");
}
// 检查连接有效性
if (!connection.isConnected()) {
try {
connection.reconnect();
} catch (JSchException e) {
currentConnections.decrementAndGet();
throw e;
}
}
return connection;
}
/**
* 归还连接
*/
public void returnConnection(SftpConnectionManager connection) {
if (connection == null || isShutdown) {
return;
}
if (connection.isConnected()) {
boolean offered = connectionPool.offer(connection);
if (!offered) {
// 池已满,关闭连接
connection.disconnect();
currentConnections.decrementAndGet();
logger.debug("连接池已满,关闭多余连接");
} else {
logger.debug("连接已归还到连接池");
}
} else {
// 连接已断开
currentConnections.decrementAndGet();
logger.debug("归还无效连接,当前连接数: {}", currentConnections.get());
}
}
/**
* 关闭连接池
*/
public void shutdown<