参考:
- SFTP搭建(密码认证、密钥认证)、客户端连接、java代码连接_青牛-CSDN博客_sftp 密码
https://blog.csdn.net/alan_gui/article/details/85220010
- 教你如何配置linux用户实现禁止ssh登陆机器但可用sftp登录!_yinn-CSDN博客
https://blog.csdn.net/qq_35440678/article/details/52788808
- 弹出Warning:Permanently added (RSA) to the list of known hosts的警告_记录我学习的点点滴滴-CSDN博客: https://blog.csdn.net/m0_37871296/article/details/91042406
- linux SSH各配置项解释 - zqifa - 博客园: https://www.cnblogs.com/zqifa/p/ssh-2.html
- ssh客户端load key“.ssh/id_rsa”:bad permissions处理 - 驴途: https://www.ofuni.com/load-key-ssh-id_rsabad-permissions/
- linux、sftp密钥免密登录_xwh159753的专栏-CSDN博客_sftp免密登录: https://blog.csdn.net/xwh159753/article/details/108116175
- SSH代理KEY转发_清风明月的技术博客_51CTO博客: https://blog.51cto.com/coolsky/1304181
- 【OpenSSH】internal-sftp和sftp-server之间的区别和联系什么? - 简书: https://www.jianshu.com/p/f24d8ca565d7
- 【linux】在linux上生成SSH-key 简单原理介绍+生成步骤 - Angel挤一挤 - 博客园: https://www.cnblogs.com/sxdcgaq8080/p/10570150.html
- linux内网SSH,SCP登录其他服务器Add correct host key in /root/.ssh/known_hosts to get rid of this message_liqi_q的博客-CSDN博客: https://blog.csdn.net/liqi_q/article/details/78465949
密钥认证需要在用户(/home/xxx/.ssh)目录下面配置密钥
chmod 555 文件夹 可以让所属sftp用户只有只读权限 没有上传删除的权限
参考:
linux 普通用户可以拥有删除root用户文件的权限吗?-森林的个人博客
https://www.liuhaolin.com/linux/324.html
Linux权限详解(chmod、600、644、666、700、711、755、777、4755、6755、7755)_wlin的博客-CSDN博客_chmod
https://blog.csdn.net/u013197629/article/details/73608613
一、SFTP 搭建
在一些情况下(如需要为商户提供对账文件),你需要一台文件服务器存放这些文件,并允许用户登陆这台文件服务器传输(上传和下载)文件,但是不允许使用 SSH 方式(如 secureCRT)登录文件服务器,你只允许其访问指定根目录(/home/sftp)下文件,这样 SFTP 服务就能很好满足这种需求。sftp 传输数据(文件)使用的依旧是 SSH 协议,linux 开启了 sshd 就相当于开启了 SFTP。注:搭建 SFTP 文件服务器不需求安装什么额外的包,除了 SSH,ssh 需要 OpenSSH4.8p1 以后的版本。因为要使用 chroot 设置根目录(用户能看见和访问的根目录)。
查看 SSH 版本
ssh -V
搭建步骤:
使用 root 操作
1、建 SFTP 组
groupadd sftp
创建完成后使用 cat /etc/group 可以看到
2、创建用户 albert
useradd -g sftp -s /sbin/nologin -M albert
-g 加入到 sftp 组
-s 禁止 ssh 登陆
-M 不要自动建立用户的登陆目录(在 / home 下)
3、为 albert 用户设置密码
passwd albert
alan123456
重复输入两次同一密码
4、创建 sftp 存放文件的目录并设置文件拥有者
mkdir -p /dala/sftp/verifyfile
chown -R albert:sftp /data/sftp/verifyfile
chmod 755 /data/sftp/verifyfile
5、编辑配置文件 / etc/ssh/sshd_config
注:修改配置文件前先备份原有正常配置文件,方面还原
cp /etc/ssh/sshd_confiig /etc/ssh/sshd_config.bak
vi /etc/ssh/sshd_config
注释掉 Subsystem sftp /usr/libexec/openssh/sftp-server
在其下面添加
Subsystem sftp internal-sftp
在文件最后添加
Match Group sftp # 指定活动目录
ChrootDirectory /data/sftp # 指定根目录
ForceCommand internal-sftp # 强制执行内部 SFTP, 并忽略任何~/.ssh/rc 中的命令
AllowTcpForwarding no # 禁用端口转发
X11Forwarding no # 禁用 X11 图形界面转发
6、重启 sshd
service sshd restart
7、测试
sftp albert@ip
输入密码即可连接上
8、权限控制
按照上面的设置
chown albert:sftp /data/sftp/verifyfile
chmod 755 /data/sftp/verifyfile
albert 是拥有 / data/sftp/verifyfile 文件的读写权限的
sftp 组其他用户的权限是 5,即没有写入权限
如新增用户 jack
useradd -g sftp -s /sbin/nologin -M jack
passwd jack
jack123456
重复输入两次同一密码
那么 jack 的权限只有 5
9、以上只是基本操作,但可基于此依据你自己需求(用户、文件目录、权限)自行设计专属 sftp 文件服务器,对于不断新增特定目录、特定权限用户的操作,可以配合 sheel 脚本使用或基于脚本再开发前端页面方面操作。
10、rsa 认证方式设置和登录
以上介绍的 SFTP 用户登录方式是 sshd_config 配置文件默认打开的密码验证方式。
PasswordAuthentication yes
再介绍一种实际中使用更多,更安全,更方面的用户登录验证方式,即密钥登录。
密钥登录无需用户设置密码,通过 rsa 密钥对加解密验证,在客户端和服务器端建立安全的连接,简单地说,public key 放在服务器端,即下面配置的 authorized_keys,private key 放在客户端,客户端发起请求连接,服务器根据请求用户名识别对应客户端公钥,sshd 服务产生一个随机数,用 public key 进行加密后,发回到客户端,客户端用 private key 解密得到该随机数,客户端将解密后的随机数发回服务器端,服务端进行匹配,匹配成功认证通过,允许登录。这种方式避免了密码暴力破解尝试的危险,当然密钥认证因为有加解密和随机数传输验证的过程,连接耗时自然比密码方式长些。
我们再新增可登录用户 qingniu , 采用密钥验证方式登录
依旧用 root 操作
useradd -g sftp -s /sbin/nologin qingniu
不要再使用 passwd 为其设置密码
注意这里不要 -M , 因为我们需要 useradd 为新增用户在 / home 目录下自动建立用户的登陆目录, 我们需要再 / home/qingniu 目录下设置密钥文件。
cd /home/qingniu
ll -a — 可看到所有隐藏目录
mkdir .ssh — 存放密钥文件和认证文件
ssh-keygen -t rsa
输入 / home/qingniu/.ssh/id_rsa_qingniu
按回车键,不对密钥加密
再次按回车确认
成功在 / home/qingniu/.ssh 目录下生成一对密钥 id_rsa_qingniu 、id_rsa_qingniu.pub

cd /home /qingniu/.ssh — 进入密钥对所在目录
cat id_rsa_qingniu.pub >> authorized_keys — 往 authorized_keys 文件中写入公钥 key
接下来就是新增目录、密钥的权限和用户组设置,很重要,因为我们是用 root 用户操作这些目录和文件的。至于为什么按下面设置,请自行思考,其实也很简单。
chmod 600 /home/qingniu/.ssh/authorized_keys
chmod 700 /home/qingniu/.ssh
chown -R qingniu:sftp /home/qingniu/.ssh
为 / etc/ssh/sshd_config 添加配置
PermitRootLogin yes
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ss:h/authorized_keys
添加完成后保存再重启 sshd 服务
service sshd restart
把私钥 id_rsa_qingniu 导出,重新打开 FileZilla 用密钥认证方式登录测试
如果你只允许用户使用密钥认证登录,可以设置 / etc/ssh/sshd_config 文件
PasswordAuthentication no
如果不改,密钥认证和密钥认证两种登录方式都允许。
二、客户端连接
FileZilla 下载地址:https://filezilla-project.org/download.php
按照安装引导安装即可
使用上面 SFTP 用户登陆测试
密码认证方式
输入 主机 IP username password port(默认 22)
密钥认证方式
编辑 -> 设置 -> sftp -> 添加密钥文件 -> 确认
输入 主机 IP username 不填密码 port(默认 22)
三、java 代码连接
使用 java 代码语言实现与 sftp 服务器的文件传输,我们可以使用 Jcraft 公司开发的 JSch 包,JSch 使用纯 java 语言实现了 ssh2 协议,感谢 ssh2 标准协议的制定,才使得我们能通过各种形态的客户端与服务器之间建立安全可靠的通信。因此实现了 SSH2 协议的 JSch 能做的也就不仅仅是文件传输,包括 ssh 协议实现的 X11 GUI 转发、端口转发、终端仿真等各种酷炫功能,所以学习 JSch 项目对于 java 程序员是有很必要的,那就从文件传输这个功能开始接触 JSch 这个项目吧。
首先在项目中引入 JSch 包
com.jcraft
jsch
0.1.53
基于 JSch 包实现的 SFTP 服务器文件上传和下载功能代码如下:
package com.qing.niu.communication.sftp;import com.jcraft.jsch.*;import org.slf4j.LoggerFactory;import org.springframework.util.Assert;import java.util.HashMap;import java.util.Properties;public static final Logger logger = LoggerFactory.getLogger(SftpTool.class);public SftpTool(String host, String username, int port){this.username = username;public Map<String,Object> loginIn(AuthTypeMode authTypeMode){ JSch.setLogger(new SettleJschLogPrint()); logger.info("获取SFTP服务器连接username:{},host:{},port:{}",username,host,port); session = jsch.getSession(username,host,port);if (AuthTypeEnum.RSA.getCode().equals(authTypeMode.getAuthType())){ jsch.addIdentity(authTypeMode.getAuthValue(),""); session.setPassword(authTypeMode.getAuthValue()); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking","no"); sshConfig.put("PreferredAuthentications","publickey,gssapi-with-mic,keyboard-interactive,password"); session.setConfig(sshConfig); logger.info("用户" + username + "成功登陆"); Channel channel = session.openChannel("sftp"); sftp = (ChannelSftp) channel; HashMap<String,Object> loginInfo = new HashMap<>(); loginInfo.put("sftp",sftp); loginInfo.put("session",session); } catch (JSchException e) {throw new RuntimeException("user login SFTP server occur exception:" + e);public void loginOut(ChannelSftp sftp, Session session){if(null != sftp && sftp.isConnected()){if (null != session && session.isConnected()){ logger.warn("用户退出SFTP服务器出现异常:" + e);public void download(String downloadFilePath, String downloadFileName, File saveFile, AuthTypeMode authTypeMode) throws Exception{ Assert.notNull(downloadFilePath,"download file absolute path is not null"); Assert.notNull(downloadFileName,"download file is not null"); Assert.notNull(saveFile,"save file location is not null"); Assert.notNull(authTypeMode,"auth type way is not null"); OutputStream outputStream = null; Map<String,Object> loginInfo = loginIn(authTypeMode); sftp = (ChannelSftp) loginInfo.get("sftp"); session = (Session) loginInfo.get("session"); logger.info("待下载文件地址为:" + downloadFilePath + ",文件名为:" + downloadFileName + ",认证方式:" + authTypeMode.getAuthValue()); sftp.cd(downloadFilePath); sftp.ls(downloadFileName); outputStream = new FileOutputStream(saveFile); sftp.get(downloadFileName,outputStream);if (null != outputStream){public void upload(String uploadPath, String uploadFile, AuthTypeMode authTypeMode) throws Exception{ Assert.notNull(uploadPath,"upload path is not null"); Assert.notNull(uploadFile,"upload file is not null"); Assert.notNull(authTypeMode,"auth type way is not null"); InputStream inputStream = null; Map<String,Object> loginInfo = loginIn(authTypeMode); sftp = (ChannelSftp) loginInfo.get("sftp"); session = (Session) loginInfo.get("session"); logger.info("待上传文件为:" + uploadFile + ",上传SFTP服务器路径:" + uploadPath + ",认证方式:" + authTypeMode.getAuthValue()); File file = new File(uploadFile); inputStream = new FileInputStream(file); } catch (SftpException e) { logger.error("SFTP器服务存放文件路径不存在");throw new RuntimeException("upload path is not exist"); sftp.put(inputStream,file.getName());if (null != inputStream){private String authValue; AuthTypeMode(String authType, String authValue){this.authType = authType;this.authValue = authValue;class SettleJschLogPrint implements com.jcraft.jsch.Logger{public boolean isEnabled(int i) {public void log(int i, String s) { PASSWORD("PASSWORD","密码认证"), AuthTypeEnum(String code, String desc){public static void main(String[] args) throws Exception{ SftpTool sftpTool = new SftpTool("192.168.79.151","albert",22); File saveFile = new File("/data/sftp/verifyfile_01.txt"); sftpTool.download("/verifyfile","verifyfile_01.csv",saveFile,sftpTool.new AuthTypeMode(AuthTypeEnum.PASSWORD.getCode(),"alan123456")); sftpTool.upload("/verifyfile","/data/sftp/verifyfile_01.txt",sftpTool.new AuthTypeMode(AuthTypeEnum.PASSWORD.getCode(),"alan123456")); SftpTool sftpToolTwo = new SftpTool("192.168.79.151","qingniu",22); File saveFileTwo = new File("/data/sftp/verifyfile_01.txt"); sftpToolTwo.download("/verifyfile","verifyfile_01.csv",saveFileTwo,sftpToolTwo.new AuthTypeMode(AuthTypeEnum.RSA.getCode(),"/data/rsa/id_rsa_qingniu")); //密钥认证方式上传文件( 上传会失败Permission denied,因为qingniu这个用户没有写的权限 ) sftpToolTwo.upload("/verifyfile","/data/sftp/verifyfile_01.txt",sftpToolTwo.new AuthTypeMode(AuthTypeEnum.RSA.getCode(),"/data/rsa/id_rsa_qingniu"));
