1. 什么是SFTP

SFTP是一个安全文件传送协议,可以为传输文件提供一种安全的加密方法。SFTP 为 SSH的一部份,是一种传输文件到服务器的安全方式。SFTP是使用加密传输认证信息和传输的数据,所以,使用SFTP是非常安全的。但是,由于这种传输方式使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。

2.java项目中使用JSch

Jsch是一个纯粹的用java实现SSH功能的java library,支持密码登录方式和秘钥登录方式。用密码登录,就是和我们用ftp的账号密码登录一样,比较简单。秘钥登录方式,就需要把客户端的公钥放到SFTP服务器,然后客户端需要用秘钥登录。

3.Jsch文件上传示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
 

import com.jcraft.jsch.*;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
* 功能简述:<br>
* 详细描述:<br>
*
* @since SFtp工具类
*/
@Data
@Configuration
@Component
public class SFtpUtils {
private static Logger log = LoggerFactory.getLogger(SFtpUtils.class);

/**
* 将指定流文件上传到FTP服务器
*
* @param ftpAddress ftp地址
* @param ftpPort ftp端口
* @param ftpUserName ftp账号
* @param ftpPassowrd ftp密码
* @param ftpFileName 上传到FTP服务器的文件名
* @param ftpPath 上传到FTP服务器指定目录下(如果目录在FTP服务器上不存在,则自动创建)
* @param localFile 本地文件流
* @param privateKeyFile 私钥
* @return
*/
public static boolean uploadFile(String ftpAddress, int ftpPort, String ftpUserName, String ftpPassowrd, String privateKeyFile, String ftpFileName, String ftpPath, InputStream localFile) throws JSchException {
boolean success = false;
ChannelSftp channelSftp = null;
Session jschSession = null;
try {
JSch jsch = new JSch();
// 私钥非空的时候使用私钥进行验证
if (StringUtils.isNotEmpty(privateKeyFile)) {
jsch.addIdentity(privateKeyFile);
}
// 获取到jSch的session, 根据用户名、主机ip、端口号获取一个Session对象
jschSession = jsch.getSession(ftpUserName, ftpAddress, ftpPort);
if (StringUtils.isNotEmpty(privateKeyFile)) {
jschSession.setUserInfo(new SftpAuthKeyUserInfo(privateKeyFile));
} else if (StringUtils.isNotEmpty(ftpPassowrd)) {
jschSession.setPassword(ftpPassowrd);
}

// 通过Session建立连接
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
jschSession.setConfig(sshConfig);
jschSession.connect();
channelSftp = (ChannelSftp) jschSession.openChannel("sftp");
// sftp管道连接
channelSftp.connect();
// 进入到目标目录,目标目录如果不存在则创建
String[] paths = ftpPath.split("/");
StringBuffer sb = new StringBuffer();
for (String p : paths) {
if (StringUtils.isEmpty(p)) {
continue;
}
sb = sb.append("/" + p);
//循环创建路径
try {
channelSftp.cd(sb.toString());
} catch (SftpException ex) {
channelSftp.mkdir(sb.toString());
channelSftp.cd(sb.toString());
}
}
// 上传文件
channelSftp.put(localFile, ftpFileName);
channelSftp.exit();
success = true;
} catch (SftpException e) {
e.printStackTrace();
log.error("IOException", e);
} finally {
if (channelSftp != null) {
channelSftp.disconnect();
}
if (jschSession != null) {
jschSession.disconnect();
}
if (localFile != null) {
try {
localFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return success;
}

/**
* 获取一个已经connect的ChannelSftp对象
* @param ftpAddress Sftp地址
* @param ftpPort 端口号
* @param ftpUserName 账号
* @param ftpPassowrd 密码
* @return
*/
public static ChannelSftp getChannelSftp(String ftpAddress, int ftpPort, String ftpUserName, String ftpPassowrd) {
ChannelSftp channelSftp = null;
Session jschSession = null;
try {
JSch jsch = new JSch();
// 获取到jSch的session, 根据用户名、主机ip、端口号获取一个Session对象
jschSession = jsch.getSession(ftpUserName, ftpAddress, ftpPort);
jschSession.setPassword(ftpPassowrd);
// 通过Session建立连接
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
jschSession.setConfig(sshConfig);
jschSession.connect();
channelSftp = (ChannelSftp) jschSession.openChannel("sftp");
// sftp管道连接
channelSftp.connect();
return channelSftp;
} catch (JSchException e) {
e.printStackTrace();
log.error("IOException", e);
if (channelSftp != null) {
channelSftp.disconnect();
}
if (jschSession != null) {
jschSession.disconnect();
}
} finally {
}
return null;
}

public static void close(ChannelSftp channelSftp) {
if (channelSftp != null) {
channelSftp.disconnect();
}
}

/**
* UserInfo是jsch包的一个接口
*/
static class SftpAuthKeyUserInfo implements UserInfo{
/**
* ssh private key passphrase
*/
private String passphrase;

/**
* 构造函数需要传秘钥的路径,是秘钥的文件路径,不是流也不是秘钥内容
*/
public SftpAuthKeyUserInfo (String passphrase) {
this.passphrase = passphrase;
}

@Override
public String getPassphrase() {
return passphrase;
}

@Override
public String getPassword() {
return null;
}

@Override
public boolean promptPassword(String message) {
return false;
}

@Override
public boolean promptPassphrase(String message) {
return true;
}

@Override
public boolean promptYesNo(String message) {
return true;
}

@Override
public void showMessage(String message) {
log.info ("SSH Message:{}", message);
}
}
}

4.秘钥登录方式怎么传文件的路径呢?

我在项目中是把秘钥文件放在了resource资源目录下。然后根据项目环境取不同环境的秘钥。

1
ClassPathResource classPathResource = new ClassPathResource(privateKeyPathEnv + "/id_rsa");String sftpPrivatePath = classPathResource.getFile().getAbsolutePath();