sftp服务器配置
点击此处
依赖
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.ethz.ganymed/ganymed-ssh2 -->
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>
ssh2工具类
package com.tianshl;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* @author tianshl
* @version 2018/5/22 下午1:29
*/
@Component
public class Ssh2Utils {
private Connection connection;
/**
* 登录
*/
public void login(String host, String username, String password) throws Exception {
connection = new Connection(host);
connection.connect();
if(!connection.authenticateWithPassword(username, password)){
throw new Exception("管理员账号或密码错误");
}
}
/**
* 退出
*/
public void logout() {
try {
if (connection != null) connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 执行指令
*/
public String execCommand(final String command) {
final StringBuilder sb = new StringBuilder(256);
// 连接的通道
Session session = null;
try {
// 创建session
session = connection.openSession();
// 这句非常重要,开启远程的客户端
session.requestPTY("vt100", 80, 24, 640, 480, null);
// 开启后睡眠4秒
// Thread.sleep(4000);
// 执行命令
session.execCommand(command);
// 起始时间,避免连通性陷入死循环
long start = System.currentTimeMillis();
BufferedReader br = new BufferedReader(new InputStreamReader(new StreamGobbler(session.getStdout())));
char[] arr = new char[512];
int read;
while (true) {
// 将结果流中的数据读入字符数组
read = br.read(arr, 0, arr.length);
// 推延5秒就退出[针对连通性测试会陷入死循环]
if (read < 0 || (System.currentTimeMillis() - start) > 5000) break;
// 将结果拼装进StringBuilder
sb.append(new String(arr, 0, read));
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
// 关闭通道
if (session != null) session.close();
}
return sb.toString();
}
}
service
package com.tianshl;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.Map;
/**
* service for sftp
*/
@Service
@Transactional
public class SftpService {
// sftp 服务器地址
@Value("${sftp.host}")
private String host;
// sftp 服务器管理员账号
@Value("${sftp.root.username}")
private String username;
// sftp 服务器管理员密码
@Value("${sftp.root.password}")
private String password;
// ssh2工具类
private final Ssh2Utils ssh2Utils;
public SftpService(Ssh2Utils ssh2Utils) {
this.ssh2Utils = ssh2Utils;
}
/**
* 创建linux用户 创建相关目录 以及 权限设置
*/
private JSONObject createUser (Ssh2Utils ssh2Utils, String sftpUsername) {
// 返回信息
JSONObject resp = new JSONObject();
resp.put("success", false);
// 家目录
String home = "/sftp/" + sftpUsername;
// 创建账号
try {
// 创建账号命令
String stdOut = ssh2Utils.execCommand("useradd -d " + home + " -m -g sftp -s /bin/false " + sftpUsername);
// 用户名已存在
if (stdOut.contains("useradd: user '" + sftpUsername + "' already exists")) {
ssh2Utils.logout();
resp.put("success", false);
resp.put("msg", "账户名称'" + sftpUsername + "'已存在,请更换其他名称.");
return resp;
}
List<String> command = Lists.newArrayList();
// 设置家目录属主
command.add("chown root:sftp " + home);
// 设置家目录权限
command.add("chmod 755 " + home);
// 创建目录 (.ssh秘钥相关, data存放数据文件)
command.add("mkdir " + home + "/{.ssh,data}");
// 新增的目录设置属主
command.add("chown " + sftpUsername + ":sftp " + home + "/{.ssh,data}");
// 新增目录设置权限
command.add("chmod 777 " + home + "/data");
command.add("chmod 700 " + home + "/.ssh");
// 执行以上指令
ssh2Utils.execCommand(String.join(";", command));
} catch (Exception e) {
e.printStackTrace();
resp.put("msg", "创建账号失败");
return resp;
}
resp.put("success", true);
return resp;
}
/**
* 生成秘钥
*/
private JSONObject genSecretKey(Ssh2Utils ssh2Utils, String sftpUsername) {
// 返回信息
JSONObject resp = new JSONObject();
resp.put("success", false);
String sshDirectory = String.format("/sftp/%s/.ssh", sftpUsername);
// 指令集
List<String> command = Lists.newArrayList();
// 删除旧的私钥
command.add(String.format("rm -f %s/id_rsa*", sshDirectory));
// 生成新的秘钥(私钥+公钥)
command.add(String.format("ssh-keygen -f %s/id_rsa -P '' -C '%s'", sshDirectory, sftpUsername));
// 添加公钥至authorized_keys
command.add(String.format("cat %s/id_rsa.pub >> %s/authorized_keys", sshDirectory, sshDirectory));
// 设置权限
command.add(String.format("chmod 600 %s/authorized_keys", sshDirectory));
// 设置属主
command.add(String.format("chown %s:sftp %s/*", sftpUsername, sshDirectory));
// 执行以上指令
try {
ssh2Utils.execCommand(String.join(";", command));
} catch (Exception e) {
e.printStackTrace();
resp.put("msg", "生成秘钥失败");
return resp;
}
resp.put("success", true);
return resp;
}
/**
* 添加秘钥
*/
private JSONObject addSecretKey(Ssh2Utils ssh2Utils, String sftpUsername, String pubKey) {
// 返回信息
JSONObject resp = new JSONObject();
resp.put("success", false);
String sshDirectory = String.format("/sftp/%s/.ssh", sftpUsername);
try {
// 将公钥添加至authorized_keys
ssh2Utils.execCommand(String.format("echo '%s' >> %s", pubKey, String.format("%s/authorized_keys", sshDirectory)));
} catch (Exception e) {
e.printStackTrace();
resp.put("msg", "添加秘钥失败");
return resp;
}
resp.put("success", true);
return resp;
}
/**
* 更新秘钥
*/
public BaseDto changeSecretKey(String sftpUsername, HttpServletResponse response, MultipartFile secretKey) {
try {
// 登录sftp服务器
ssh2Utils.login(host, username, password);
JSONObject resp;
// 生成秘钥
if (secretKey == null) {
// 为我生成一个秘钥
resp = genSecretKey(ssh2Utils, sftpUsername);
if (resp.getBoolean("success")) {
downloadSecretKey(id, response);
}
} else {
// 使用我自己的秘钥
resp = addSecretKey(ssh2Utils, sftpUsername, new String(secretKey.getBytes()));
}
if (!resp.getBoolean("success")) {
ssh2Utils.logout();
return BaseDto.error(resp.getString("msg"), null);
}
ssh2Utils.logout();
} catch (Exception e){
e.printStackTrace();
}
return BaseDto.success("设置成功", null);
}
/**
* 下载秘钥
*/
public BaseDto downloadSecretKey(String sftpUsername, HttpServletResponse response){
String sshDirectory = String.format("/sftp/%s/.ssh", sftpUsername);
// 下载秘钥
try {
// 登录sftp服务器
ssh2Utils.login(host, username, password);
// 获取私钥文件内容
String secret = ssh2Utils.execCommand(String.format("cat %s/id_rsa", sshDirectory));
// 私钥内容结束标志
String end = "-----END RSA PRIVATE KEY-----";
// 截取私钥内容 (去除其他无关的日志打印内容)
secret = secret.substring(0, secret.lastIndexOf(end) + end.length());
// 以附件形式下载私钥
response.setContentType("text/plain");
response.setHeader("Content-Disposition", "attachment; filename=\"id_rsa\"");
PrintWriter pw = response.getWriter();
pw.write(secret);
pw.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ssh2Utils.logout();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 新增sftp账户
*/
public BaseDto save(String sftpUsername, MultipartFile secretKey) {
// 校验名称
if (StringUtils.isRealBlank(sftpUsername)) {
return BaseDto.error("账户名称不能为空!", null);
}
try {
// 登录sftp服务器
ssh2Utils.login(host, username, password);
// 创建账号
JSONObject resp = createUser(ssh2Utils, sftpUsername);
if (!resp.getBoolean("success")) {
return BaseDto.error(resp.getString("msg"), null);
}
// 生成秘钥
if (secretKey == null) {
// 为我生成一个秘钥
resp = genSecretKey(ssh2Utils, sftpUsername);
if (resp.getBoolean("success")) return null;
} else {
// 使用我自己的秘钥
resp = addSecretKey(ssh2Utils, sftpUsername, new String(secretKey.getBytes()));
}
if (!resp.getBoolean("success")) {
return BaseDto.error(resp.getString("msg"), null);
}
} catch (Exception e){
e.printStackTrace();
} finally {
try {
ssh2Utils.logout();
} catch (Exception e) {}
}
return BaseDto.success("创建成功", null);
}