2018/12/14 杨贤宾
一、SFTP使用场景
ftp是大多数网站的文件传输选择工具,但ftp并不是非常安全,并且在centos上搭建的vsftpd也非常的不稳定,偶尔会出现权限问题,例如500、或是账号密码不正确等等。
而SFTP是基于默认的22端口,是ssh内含的协议,只要启动了sshd就可以使用。
建议:更高的效率使用FTP协议,更安全的通信使用SFTP协议。
FTP是一种文件传输协议,一般是为了方便数据共享的。包括一个FTP服务器和多个FTP客户端。FTP客户端通过FTP协议在服务器上下载资源。而SFTP协议是在FTP的基础上对数据进行加密,使得传输的数据相对来说更安全。但是这种安全是以牺牲效率为代价的,也就是说SFTP的传输效率比FTP要低(不过现实使用当中,没有发现多大差别)。个人肤浅的认为就是:
FTP要安装,SFTP不要安装。
SFTP更安全,但更安全带来副作用就是的效率比FTP要低些。
二、centos7搭建SFTP
创建sftp组
查看组信息 groupadd sftp
cat /etc/group
创建一个sftp用户mysftp并加入到创建的sftp组中,同时修改mysftp用户的密码 useradd -g sftp -s /bin/false mysftp
passwd mysftp
新建目录,指定为mysftp用户的主目录 mkdir -p /sftp/mysftp
usermod -d /sftp/mysftp mysftp
编辑配置文件/etc/ssh/sshd_config
将如下这行用#符号注释掉 # Subsystem
sftp /usr/libexec/openssh/sftp-server
并在文件最后面添加如下几行内容然后保存(最好放在文件末尾) Subsystem
sftp internal-sftp
Match Group sftp
ChrootDirectory /sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
设置Chroot目录权限 chown root:sftp /sftp/mysftp #文件夹所有者必须为root,用户组可以不是root
chmod 755 /sftp/mysftp #权限不能超过755,否则会导致登录报错,可以是755
新建一个目录供stp用户mysftp上传文件,这个目录所有者为mysftp所有组为sftp,所有者有写入权限所有组无写入权限 mkdir /sftp/mysftp/upload
chown mysftp:sftp /sftp/mysftp/upload
chmod 755 /sftp/mysftp/upload
关闭selinux并重启sshd服务,然后测试 setenforce 0
service sshd restart
在其他服务器上进行验证,sftp 用户名@ip地址 sftp -P 10001 mysftp@127.0.0.1
三、Windows搭建
下载安装freesshd
下载地址:
下载后安装,一路点击确认就好,安装时产生key,直接点击确认
安装软件后,以管理员身份打开
在ssh中地址可以选在本地地址,端口选在22端口,安装之后可以看到key是默认有的
新增用户
更改认证方式
更改sftp服务器根目录
启动服务
如果上一步无法启动,应该是之前已经启动,端口被占用,需要进入服务,重新启动
下载sftp链接工具(xftp或Filezilla等)
测试成功
异常及解决方案
输入正确用户名和密码提示denied或者key verification failed.,常规解决方案如下
删除C:\Users\Administrator下的.ssh文件夹,使用管理员程序重新运行freesshd,再次测试连接
四、java代码连接
package com.kehua.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Vector;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
/**
* 描述: sftp工具类
* @date 2018年12月11日
* @author 杨贤宾
* @version 1.0
*/
public class SFTPUtil {
private transient Logger log = LoggerFactory.getLogger(this.getClass());
private ChannelSftp sftp;
private Session session;
/** SFTP 登录用户名*/
private String username;
/** SFTP 登录密码*/
private String password;
/** 私钥 */
private String privateKey;
/** SFTP 服务器地址IP地址*/
private String host;
/** SFTP 端口*/
private int port;
/**
* 构造基于密码认证的sftp对象
*/
public SFTPUtil(String username, String password, String host, int port) {
this.username = username;
this.password = password;
this.host = host;
this.port = port;
}
/**
* 构造基于秘钥认证的sftp对象
*/
public SFTPUtil(String username, String host, int port, String privateKey) {
this.username = username;
this.host = host;
this.port = port;
this.privateKey = privateKey;
}
public SFTPUtil(){}
/**
* 连接sftp服务器
*/
public void login(){
try {
JSch jsch = new JSch();
if (privateKey != null) {
jsch.addIdentity(privateKey);// 设置私钥
}
session = jsch.getSession(username, host, port);
if (password != null) {
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
} catch (JSchException e) {
e.printStackTrace();
}
}
/**
* 关闭连接 server
*/
public void logout(){
if (sftp != null) {
if (sftp.isConnected()) {
sftp.disconnect();
}
}
if (session != null) {
if (session.isConnected()) {
session.disconnect();
}
}
}
/**
* 将输入流的数据上传到sftp作为文件。文件完整路径=basePath+directory
* @param basePath 服务器的基础路径
* @param directory 上传到该目录
* @param sftpFileName sftp端文件名
* @param in 输入流
*/
public void upload(String basePath,String directory, String sftpFileName, InputStream input) throws SftpException{
try {
sftp.cd(basePath);
sftp.cd(directory);
} catch (SftpException e) {
//目录不存在,则创建文件夹
String [] dirs=directory.split("/");
String tempPath=basePath;
for(String dir:dirs){
if(null== dir || "".equals(dir)) continue;
tempPath+="/"+dir;
try{
sftp.cd(tempPath);
}catch(SftpException ex){
sftp.mkdir(tempPath);
sftp.cd(tempPath);
}
}
}
sftp.put(input, sftpFileName); //上传文件
}
/**
* 下载文件。
* @param directory 下载目录
* @param downloadFile 下载的文件
* @param saveFile 存在本地的路径
*/
public void download(String directory, String downloadFile, String saveFile) throws SftpException, FileNotFoundException{
if (directory != null && !"".equals(directory)) {
sftp.cd(directory);
}
File file = new File(saveFile);
sftp.get(downloadFile, new FileOutputStream(file));
}
/**
* 下载文件
* @param directory 下载目录
* @param downloadFile 下载的文件名
* @return 字节数组
*/
public byte[] download(String directory, String downloadFile) throws SftpException, IOException{
if (directory != null && !"".equals(directory)) {
sftp.cd(directory);
}
InputStream is = sftp.get(downloadFile);
byte[] fileData = IOUtils.toByteArray(is);
return fileData;
}
/**
* 删除文件
* @param directory 要删除文件所在目录
* @param deleteFile 要删除的文件
*/
public void delete(String directory, String deleteFile) throws SftpException{
sftp.cd(directory);
sftp.rm(deleteFile);
}
/**
* 列出目录下的文件
* @param directory 要列出的目录
* @param sftp
*/
public Vector<?> listFiles(String directory) throws SftpException {
return sftp.ls(directory);
}
public static void main(String[] args) throws SftpException, IOException {
SFTPUtil sftp = new SFTPUtil("userftp", "khyf3@123", "112.74.59.0", 10001);
sftp.login();
//上传文件测试
/*File file = new File("E:\\谷歌下载资源\\linux 安装 mysql.html");
InputStream is = new FileInputStream(file);
sftp.upload("/upload","code", "linux 安装 mysql.html", is);
sftp.logout(); */
//下载文件测试
sftp.download("/upload/code", "CODE_0000000000000020.png" ,"D:\\2.PNG");
sftp.logout();
}
}