Java建立sftp帳戶並設置祕鑰

sftp服務器配置

點擊此處java

依賴

<!-- 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);
    }
相關文章
相關標籤/搜索