精华内容
下载资源
问答
  • java实现SSH协议连接linux

    千次阅读 2020-07-06 19:49:39
    所以采用java代码的方式连接服务器实现自动上传换版本启动的过程。。 首先: 使用ssh协议的jar引进工程pom依赖: <dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId

    初衷:

    在每次部署项目的时候,每次都得打包,使用xshell连接linux服务器,每次在连接的时候都会很慢,还得杀死原先的进程id并删除之前的版本,很烦!!所以采用java代码的方式连接服务器实现自动上传换版本启动的过程。。


    首先:

    使用ssh协议的jar引进工程pom依赖:

            <dependency>
                <groupId>ch.ethz.ganymed</groupId>
                <artifactId>ganymed-ssh2</artifactId>
                <version>build210</version>
            </dependency>

    可能本地中远程仓库中没有改依赖包,解决方法:首先去这个网站(http://www.ganymed.ethz.ch/ssh2/)去下载,然后呢把他添加到自己的本地仓库中,具体操作方法如下:

    首先下载的是一个zip文件,解压文件,里面有一个jar包,然后把该jar包加入到本地仓库中:

    mvn install:install-file -Dfile=D:\software\Maven\ganymed-ssh2-build210\ganymed-ssh2-build210.jar -DgroupId=ch.ethz.ganymed -DartifactId=ganymed-ssh2 -Dversion=build210 -Dpackaging=jar

    然后在添加依赖就ojbk了。

    下面就是代码实现:

    代码实现: 

    1、实体类:

    public class RemoteConnect {
        private String ip;
        private String userName;
        private String password;
    //省略 set
    get
    |

     2、业务代码:

    import ch.ethz.ssh2.Connection;
    import ch.ethz.ssh2.SCPClient;
    import ch.ethz.ssh2.Session;
    import ch.ethz.ssh2.StreamGobbler;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.*;
    
    public class RemoteSSH {
    
        private static final Logger logger = LoggerFactory.getLogger(RemoteSSH.class);
        private static String DEFAULTCHARTSET = "UTF-8";
        private static Connection conn;
        /**
         * 远程登录linux 服务器
         *
         * @param remoteConnect
         * @return
         */
        public static boolean login(RemoteConnect remoteConnect) {
            boolean flag = false;
            try {
                conn = new Connection(remoteConnect.getIp());
                conn.connect();//连接
                flag = conn.authenticateWithPassword(remoteConnect.getUserName(), remoteConnect.getPassword());
                if (flag) {
                    logger.info("---认证成功-----");
                } else {
                    logger.error("------认证失败-----");
                    conn.close();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return flag;
        }
    
        /**
         * @param remoteConnect
         * @param keyFile       一个文件对象指向一个文件,该文件包含OpenSSH密钥格式的用户的DSA或RSA私钥(PEM,不能丢失"
         *                      -----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"标签
         * @param keyfilePass   如果秘钥文件加密 需要用该参数解密,如果没有加密可以为null
         * @return Boolean
         * @throws
         * @Title: loginByKey
         * @Description: 秘钥方式  远程登录linux服务器
         */
        public static Boolean loginByFileKey(RemoteConnect remoteConnect, File keyFile, String keyfilePass) {
            boolean flag = false;
            // 输入密钥所在路径
            // File keyfile = new File("C:\\temp\\private");
            try {
                conn = new Connection(remoteConnect.getIp());
                conn.connect();
                // 登录认证
                flag = conn.authenticateWithPublicKey(remoteConnect.getUserName(), keyFile, keyfilePass);
                if (flag) {
                    logger.info("认证成功!");
                } else {
                    logger.info("认证失败!");
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return flag;
        }
    
        /**
         * @param remoteConnect
         * @param keys          一个字符[],其中包含用户的DSA或RSA私钥(OpenSSH密匙格式,您不能丢失“
         *                      ----- begin DSA私钥-----”或“-----BEGIN RSA PRIVATE KEY-----“标签。char数组可以包含换行符/换行符。
         * @param keyPass       如果秘钥字符数组加密  需要用该字段解密  否则不需要可以为null
         * @return Boolean
         * @throws
         * @Title: loginByCharsKey
         * @Description: 秘钥方式  远程登录linux服务器
         */
        public static Boolean loginByCharsKey(RemoteConnect remoteConnect, char[] keys, String keyPass) {
    
            boolean flag = false;
            // 输入密钥所在路径
            // File keyfile = new File("C:\\temp\\private");
            try {
                conn = new Connection(remoteConnect.getIp());
                conn.connect();
                // 登录认证
                flag = conn.authenticateWithPublicKey(remoteConnect.getUserName(), keys, keyPass);
                if (flag) {
                    logger.info("认证成功!");
                } else {
                    logger.info("认证失败!");
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return flag;
        }
    
        public static String execute(String cmd) {
            String result = "";
            try {
                Session session = conn.openSession();// 打开一个会话
                session.execCommand(cmd);// 执行命令;
                result = processStdout(session.getStdout(), DEFAULTCHARTSET);
                // 如果为得到标准输出为空,说明脚本执行出错了 StringUtils.isBlank(result)
                if (result.equals("")) {
                    result = processStdout(session.getStderr(), DEFAULTCHARTSET);
                }
    //            conn.close();
    //            session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
    
        }
    
        /**
         * @param in      输入流对象
         * @param charset 编码
         * @return String 以纯文本的格式返回
         * @throws
         * @Title: processStdout
         * @Description: 解析脚本执行的返回结果
         */
        public static String processStdout(InputStream in, String charset) {
            InputStream stdout = new StreamGobbler(in);
            StringBuffer buffer = new StringBuffer();
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(stdout, charset));
                String line = null;
                while ((line = br.readLine()) != null) {
                    buffer.append(line + "\n");
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return buffer.toString();
        }
    
        /**
         * @param cmd 脚本或者命令
         * @return String 命令执行成功后返回的结果值,如果命令执行失败,返回空字符串,不是null
         * @throws
         * @Title: executeSuccess
         * @Description: 远程执行shell脚本或者命令
         */
        public static String executeSuccess(String cmd) {
            String result = "";
            try {
                Session session = conn.openSession();// 打开一个会话
                session.execCommand(cmd);// 执行命令
                result = processStdout(session.getStdout(), DEFAULTCHARTSET);
                conn.close();
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }
    
        /**
         * @return String
         * @throws
         * @Title: ConnectLinux
         * @Description: 通过用户名和密码关联linux服务器
         */
        public static String connectLinux(String ip, String userName,
                                          String password, String commandStr) {
    
            logger.info("ConnectLinuxCommand  scpGet===" + "ip:" + ip + "  userName:" + userName + "  commandStr:"
                    + commandStr);
    
            String returnStr = "";
    //        boolean result = true;
    
            RemoteConnect remoteConnect = new RemoteConnect();
            remoteConnect.setIp(ip);
            remoteConnect.setUserName(userName);
            remoteConnect.setPassword(password);
            try {
                if (login(remoteConnect)) {
                    returnStr = execute(commandStr);
                    System.out.println(returnStr);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    //        if (returnStr != null && !returnStr.equals("")) {
    //            result = false;
    //        }
            return returnStr;
        }
    
        /**
         * @param ip         (其他服务器)
         * @param userName   用户名(其他服务器)
         * @param password   密码(其他服务器)
         * @param remoteFile 文件位置(其他服务器)
         * @param localDir   本服务器目录
         * @return void
         * @throws IOException
         * @throws
         * @Title: scpGet
         * @Description: 从其他服务器获取文件到本服务器指定目录
         */
        public static void scpGet(String ip, String userName,
                                  String password, String remoteFile, String localDir)
                throws IOException {
    
            logger.info("ConnectLinuxCommand  scpGet===" + "ip:" + ip + "  userName:" + userName + "  remoteFile:"
                    + remoteFile + "  localDir:" + localDir);
            RemoteConnect remoteConnect = new RemoteConnect();
            remoteConnect.setIp(ip);
            remoteConnect.setUserName(userName);
            remoteConnect.setPassword(password);
    
            if (login(remoteConnect)) {
                SCPClient client = new SCPClient(conn);
                client.get(remoteFile, localDir);
                conn.close();
            }
        }
    
        /**
         * @param ip
         * @param userName
         * @param password
         * @param localFile
         * @param remoteDir
         * @return void
         * @throws IOException
         * @throws
         * @Title: scpPut
         * @Description: 将文件复制到其他计算机中
         */
        public static void scpPut(String ip, String userName,
                                  String password, String localFile, String remoteDir)
                throws IOException {
            logger.info("ConnectLinuxCommand  scpPut===" + "ip:" + ip + "  userName:" + userName + "  localFile:"
                    + localFile + "  remoteDir:" + remoteDir);
    
            RemoteConnect remoteConnect = new RemoteConnect();
            remoteConnect.setIp(ip);
            remoteConnect.setUserName(userName);
            remoteConnect.setPassword(password);
    
            if (login(remoteConnect)) {
                //检测指定路径下的jar是否存在
                SCPClient client = new SCPClient(conn);
                client.put(localFile, remoteDir);
                System.out.println("aa");
                conn.close();
            }
        }
    
        //重新打包上传启动功能
        public static String allDeploy(String ip, String userName,
                                       String password) {
            RemoteConnect connect = new RemoteConnect(ip, userName, password);
            if (login(connect)) {
                String commandLine = "ps -ef|grep dailybuild-|grep -v grep|wc -l";
                String execute = execute(commandLine);
                if (execute.equals("1\n")) {
                    String str = "ps -ef|grep dailybuild-|grep -v grep |awk '{print $2}' | xargs kill -9;";
                    String result = execute(str);
    //                String deleteStr = "cd /export;rm -rf dailybuild-0.0.1-SNAPSHOT.jar";
                    String deleteStr = "find / -name dailybuild-0.0.1-SNAPSHOT.jar | xargs rm -rf ";
                    execute(deleteStr);
                    SCPClient client = new SCPClient(conn);
                    try {
                        client.put("D:\\Project\\dailybuildV3_New\\target\\dailybuild-0.0.1-SNAPSHOT.jar", "/export");
                        logger.info("-----上传jar包成功----开始启动----");
                        execute("nohup java -jar /export/dailybuild-0.0.1-SNAPSHOT.jar > /export/dailybuild.log &");
                        String startLog = execute("tail -f /export/dailybuild.log");
                        if (startLog.contains("Started DailyBuildApplication"))
                            logger.info("-----完成启动----");
                        conn.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else {
                    String isExist = "find / -name dailybuild-0.0.1-SNAPSHOT.jar";
                    String result = execute(isExist);
                    if (result != "") execute("nohup java -jar " + result + ">/export/dailybuild.log &");
                }
            }
            return "完成";
        }
    
    }

    3、测试代码类:

    public class RemoteTest {
        private static String ip = "10.85.153.27";
        private static String userName = "root";
        private static String password = "#TESTzdh27";
    
        public static void main(String[] args) throws IOException {
    //        RemoteConnect connect = new RemoteConnect("10.85.153.27", "root", "#TESTzdh27");
    //        boolean login = RemoteSSH.login(connect);
    //        System.out.println(login);
    
    //        String commandStr = "smtpnum = 'lsof -i:8091 | wc -l';print $smtpnum";
    //        String result = RemoteSSH.connectLinux("10.85.153.27", "root", "#TESTzdh27", commandStr);
    //        System.out.println(result);
            //下载
    //        RemoteSSH.scpGet(ip, userName, password, "/home/shell/protect.sh", "D:\\java");
    //        RemoteSSH.scpPut(ip,userName,password,"D:\\Project\\dailybuildV3_New\\target\\dailybuild-0.0.1-SNAPSHOT.jar","/home/shell");
            RemoteSSH.allDeploy(ip, userName, password);
        }
    }

     结论:测试没得问题,这种方法呢其实跟我们用xshell直接去连接服务器是一样的,只不过速度相对快一点,java中代码也是跟在客户端敲的代码是一样的,初步目标实现了。

     

    展开全文
  • java进行ssh协议连接的jar包,jsch-0.1.54.jar,使用这一个jar即可
  • JAVA 使用ssh协议连接时的问题程序连接 juniper 防火墙时,抛出以下异常get policyjava.io.IOException: The execute request failed.at ch.ethz.ssh2.channel.ChannelManager.requestExecCommand(ChannelManager....

    JAVA 使用ssh协议连接时的问题

    程序连接 juniper 防火墙时,抛出以下异常

    get policy

    java.io.IOException: The execute request failed.

    at ch.ethz.ssh2.channel.ChannelManager.requestExecCommand(ChannelManager.java:703)

    at ch.ethz.ssh2.Session.execCommand(Session.java:249)

    at TestSshConnect.main(TestSshConnect.java:45)

    Caused by: java.io.IOException: This SSH2 channel is not open (Close requested by remote)

    at ch.ethz.ssh2.channel.ChannelManager.waitForChannelSuccessOrFailure(ChannelManager.java:174)

    at ch.ethz.ssh2.channel.ChannelManager.requestExecCommand(ChannelManager.java:699)

    ... 2 more

    源程序代码如下:

    public class TestSshConnect {

    /**

    * @param args

    * @throws FileNotFoundException

    */

    public static void main(String[] args) throws FileNotFoundException {

    Connection conn = null;

    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    conn=new Connection("192.168.2.230",22);

    try {

    conn.connect();

    conn.authenticateWithPassword("admin", "admin1234");

    SCPClient ftp = conn.createSCPClient();

    while(true)

    {

    Session s=conn.openSession();

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    byte[] bytes = new byte[1024];

    String str=reader.readLine();

    s.execCommand(str);

    InputStream stdout = new StreamGobbler(s.getStdout());

    int count;

    while ((count = stdout.read(bytes)) != -1){

    buffer.write(bytes, 0, count);

    if(count==1024){

    System.out.println(new String(bytes));

    }else{

    byte[] bs2=new byte[count];

    for(int j=0;j

    {

    bs2[j]=bytes[j];

    }

    System.out.println(new String(bs2));

    }

    }

    System.out.println(new String(bytes));

    if(s!=null)

    {s.close();}

    }

    } catch (IOException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    }

    请问 上面的问题如何解决!

    67b0a70febe552c922c54bb8560d6ef6.png

    dc7182e7ec2463169a4365531097cbaf.png

    span name=whlm id=whlm文件传输协议(File Transfer Protocol, FTP)FTP是用于在网络上进行文件传输的一套标准协议。WBR它属于网络协议组的应用层。

    FTP是一个8位的客户端-服务器协议,WBR能操作任何类型的文件而不需要进一步处理,WBR就像MIME或Unencode一样。但是,WBRFTP有着极高的延时,这意味着,WBR从开始请求到第一次接收需求数据之间的时间会非常长,WBR并且不时的必需执行一些冗长的登陆进程。

    概述

    FTP服务一般运行在20和21两个端口。WBR端口20用于在客户端和服务器之间传输数据流,WBR而端口21用于传输控制流,并且是命令通向ftp服务器的进口。WBR当数据通过数据流传输时,控制流处于空闲状态。WBR而当控制流空闲很长时间后,客户端的防火墙会将其会话置为超时,WBR这样当大量数据通过防火墙时,会产生一些问题。此时,WBR虽然文件可以成功的传输,但因为控制会话会被防火墙断开,WBR传输会产生一些错误。

    FTP实现的目标:

    促进文件的共享(计算机程序或数据)

    鼓励间接或者隐式的使用远程计算机

    向用户屏蔽不同主机中各种文件存储系统的细节

    可靠和高效的传输数据

    缺点:

    密码和文件内容都使用明文传输,可能产生不希望发生的窃听。

    因为必需开放一个随机的端口以建立连接,当防火墙存在时,WBR客户端很难过滤处于主动模式下的FTP流量。WBR这个问题通过使用被动模式的FTP得到了很大解决。

    服务器可能会被告知连接一个第三方计算机的保留端口。

    FTP虽然可以被终端用户直接使用,WBR但是它是设计成被FTP客户端程序所控制。

    运行FTP服务的许多站点都开放匿名服务,在这种设置下,WBR用户不需要帐号就可以登录服务器,默认情况下,WBR匿名用户的用户名是:“anonymous”。WBR这个帐号不需要密码,WBR虽然通常要求输入用户的邮件地址作为认证密码,WBR但这只是一些细节或者此邮件地址根本不被确定,WBR而是依赖于FTP服务器的配置情况。

    [编辑] 主动和被动模式

    FTP有两种使用模式:主动和被动。WBR主动模式要求客户端和服务器端同时打开并且监听一个端口以建立连WBR接。在这种情况下,客户端由于安装了防火墙会产生一些问题。WBR所以,创立了被动模式。WBR被动模式只要求服务器端产生一个监听相应端口的进程,WBR这样就可以绕过客户端安装了防火墙的问题。

    一个主动模式的FTP连接建立要遵循以下步骤:

    客户端打开一个随机的端口(端口号大于1024,在这里,WBR我们称它为x),WBR同时一个FTP进程连接至服务器的21号命令端口。此时,WBR源端口为随机端口x,在客户端,远程端口为21,在服务器。

    客户端开始监听端口(x+1),同时向服务器发送一个端口命令(WBR通过服务器的21号命令端口),WBR此命令告诉服务器客户端正在监听的端口号并且已准备好从此端口接WBR收数据。这个端口就是我们所知的数据端口。

    服务器打开20号源端口并且建立和客户端数据端口的连接。此时,WBR源端口为20,远程数据端口为(x+1)。

    客户端通过本地的数据端口建立一个和服务器20号端口的连接,WBR然后向服务器发送一个应答,告诉服务器它已经建立好了一个连接。

    [编辑] FTP和网页浏览器

    大多数最新的网页浏览器和文件管理器都能和FTP服务器建立连接WBR。这使得在FTP上通过一个接口就可以操控远程文件,WBR如同操控本地文件一样。WBR这个功能通过给定一个FTP的URL实现,形如ftp://<WBR服务器地址>(例如,a rel="nofollow" href="ftp://ftp.gimp.org" target="_blank"ftp://ftp.gimp.org/a )。是否提供密码是可选择的,如果有密码,则形如ftp://<WBRlogin>:<password>@<WBRftpserveraddress>。WBR大部分网页浏览器要求使用被动FTP模式,WBR然而并不是所有的FTP服务器都支持被动模式。

    /span

    10-04-11

    | 添加评论

    | 打赏

    ◆◆

    请登录后再发表评论!

    ◆◆

    展开全文
  • Java codepackage ...import java.io.IOException;import java.io.InputStream;import java.nio.charset.Charset;import java.util.ArrayList;import java.util.List;import org.apache.c...

    Java codepackage com.friendone.broadband.util;

    import java.io.IOException;

    import java.io.InputStream;

    import java.nio.charset.Charset;

    import java.util.ArrayList;

    import java.util.List;

    import org.apache.commons.io.IOUtils;

    import com.friendone.broadband.domain.ExecInfo;

    import ch.ethz.ssh2.ChannelCondition;

    import ch.ethz.ssh2.Connection;

    import ch.ethz.ssh2.Session;

    import ch.ethz.ssh2.StreamGobbler;

    public class SshConnect {

    private  Connection conn;

    /** */ /**  远程机器IP  */

    private  String    ip;

    /** */ /**  用户名  */

    private  String    usr;

    /** */ /**  密码  */

    private  String    psword;

    private  String    charset  =  Charset.defaultCharset().toString();

    private  static  final  int  TIME_OUT  =  1000  *  5  *  60 ;

    /** */ /**

    * 构造函数

    *  @param  param 传入参数Bean 一些属性的getter setter 实现略

    */

    public  SshConnect(ExecInfo param)  {

    this .ip  =  param.getIp();

    this .usr  =  param.getUser();

    this .psword  =  param.getPassword();

    }

    /** */ /**

    * 构造函数

    *  @param  ip

    *  @param  usr

    *  @param  ps

    */

    public  SshConnect(String ip, String usr, String ps)  {

    this .ip  =  ip;

    this .usr  =  usr;

    this .psword  =  ps;

    }

    /** */ /**

    * 登录

    *

    *  @return

    *  @throws  IOException

    */

    private  boolean  login()  throws  IOException  {

    conn  =  new  Connection(ip);

    conn.connect();

    return  conn.authenticateWithPassword(usr, psword);

    }

    /** */ /**

    * 执行脚本

    *

    *  @param  cmds

    *  @return

    *  @throws  Exception

    */

    public  List exec(String cmds)  throws  Exception  {

    List list= new ArrayList ();

    InputStream stdOut  =  null ;

    InputStream stdErr  =  null ;

    String outStr  =  "" ;

    展开全文
  • 简介: 本文首先对 SSH 协议的基础知识作以介绍,然后结合相关的 Java 代码逐步展开对登录认证方式的讨论。本文利于读者对 SSH 登录认证方式原理的理解,更有益于读者在实际项目中对 SSH 协议的应用。 背景 在...

    摘自:http://blog.sae.sina.com.cn/archives/333/comment-page-1#comment-37391


    简介: 本文首先对 SSH 协议的基础知识作以介绍,然后结合相关的 Java 代码逐步展开对登录认证方式的讨论。本文利于读者对 SSH 登录认证方式原理的理解,更有益于读者在实际项目中对 SSH 协议的应用。
    背景

    在开篇之前,让我们先对 SSH 协议有个宏观的大致了解,这样更有利于我们对本文的加深了解。首先要提到的就是计算机网络协议,所谓计算机网络协议,简单的说就是定义了一套标准和规则,使得不同计算机之间能够进行正常的网络通信,不至于出现在一台机器上发出的指令到另一台机器上成了不可认的乱码,SSH 就是众多协议的其中之一。经典的七层 OSI 模型(Open System Interconnection Reference Model)出现后,大大地解决了网络互联的兼容性问题,它将网络划分成服务、接口和协议三个部分,而协议就是说明本层的服务是如何实现的。SSH、Telnet 协议则主要被使用在用户层中(如图 1 深色部分所示),即应用层、表现层和会话层。


    图 1. 七层 OSI 模型 

    介绍 SSH

    什么是 SSH

    SSH(Secure Shell Protocol)是在一个不安全的网络,进行安全远程登录和其他安全网络服务的协议。这个定义出自于 IETF(Internet Engineering Task Force)。在 TCP/IP 五层模型中,SSH 是被应用于应用层和传输层的安全协议。

    SSH 的优点

    传统的网络传输,如:Telnet、FTP 等,采用的是明文传输数据和口令,这样很容易被黑客这样的中间人嗅探到传输过程中的数据,大大降低了网络的通信安全。而 SSH 协议则采用数据加密的方式建立起一个安全的网络传输信道,增强了数据在网络传输过程中的安全性。数据加密程度的复杂,会导致占用更多的网络资源。SSH 会对加密数据进行一定的压缩操作,从而减缓对网络带宽的占用。总结起来,SSH 的优点如下:

    • 数据加密,提高安全性
    • 数据压缩,提高网络的传输速度。
    SSH 的架构

    在对 SSH 有了一个初步的认识之后,我们来看看 SSH 协议是如何做到数据的安全通信。首先来看下 SSH 协议的主要架构:
    图 2. SSH 协议的构成

    传输层协议: 通常运行在 TCP/IP 的上层,是许多安全网络服务的基础,提供了数据加密、压缩、服务器认证以及保证数据的完整性。比如,公共密钥算法、对称加密算法、消息验证算法等。

    用户认证协议:运行在 SSH 协议的传输层之上,用来检测客户端的验证方式是否合法。

    连接协议:运行在用户认证层之上,提供了交互登录会话、远程命令的执行、转发 TCP/IP 连接等功能,给数据通讯提供一个安全的,可靠的加密传输信道。

    SSH 的应用

    在实际的工作中,很多目标机器往往是我们无法直接操作的,这些机器可能是一个公司机房的服务器,也可能是一个远在大洋彼岸的客户环境。这时候我们必须要远程登录到目标机器,执行我们需要的操作,这样不仅降低了运营成本,也提高了执行效率。我们常见的远程登录协议有 SSH、Telnet 等。如上文所提到,Telnet 使用的是明文传输,这样对别有用心的“中间人”来说就有了可乘之机,相对 Telnet 协议,SSH 协议的安全性就高了很多。这样的特性,也使得 SSH 协议迅速被推广,很多的大型项目中都或多或少的使用到了这个协议。下面本文主要讨论 SSH 协议中用户认证协议层,并且下文中统一将远程机器称为服务器(Server),本地机器称为客户端 (Client)。

    SSH 的认证协议

    常见的 SSH 协议认证方式有如下几种:

    • 基于口令的验证方式(password authentication method),通过输入用户名和密码的方式进行远程机器的登录验证。
    • 基于公共密钥的安全验证方式(public key authentication method),通过生成一组密钥(public key/private key)来实现用户的登录验证。
    • 基于键盘交互的验证方式(keyboard interactive authentication method),通过服务器向客户端发送提示信息,然后由客户端根据相应的信息通过手工输入的方式发还给服务器端。
    SSH 认证协议的工作原理
    SSH 的主要工作流程:
    图 3. SSH 登录工作流程
    通过这个张流程图,我们可以看出,在用户对远程机器访问的时候,首先,是得到了服务器端的一个连接句柄,这里可以理解为是一个 session,然后客户端可以通过这个句柄取得一些服务器的基本信息,如 SSH 的版本,服务器的版本信息以及一些加密的算法信息等。其次,客户端可以对这些信息作分析,来匹配当前的客户端的加密算法、验证方式是否符合服务器的配置,然后取得彼此可接受的方式,这里可以认为是双方的协商。最后,当双方达成一致后,一个安全的信道也就真正建立起来了,此时用户就可以对远程机器做想要的操作了。当我们对此有了一定的了解后,就可以初步判断,在平时工作中,我们通过 SSH 协议去连接一个远程机器报错的时候,问题出现在哪个流程上。下面通过具体的 Java 例子来讲解用户验证方式的原理。

    常见认证方式的 Java 实现

    在开始前,我们要做一些环境的准备工作。

    • 一台本地机器,操作系统是 Windows 用来作为客户端
    • 一台远程机器,操作系统是 Linux 用来作为服务器端
    • OpenSSH 工具
    • Putty 工具

    首先,要确保服务器端上已经安装了 OpenSSH 工具,并且 SSH 的服务已经启动,可以通过如下命令来进行查看:

    查看是否已经安装了 OpenSSH

    清单 1. OpenSSH 版本

    # rpm -qa | grep ssh
     openssh-5.1p1-41.31.36
     openssh-askpass-5.1p1-41.31.36

    查看 SSH 服务是否启动。

    清单 2. SSH 的服务状态

    #/etc/init.d/sshd status

    Checking for service sshd running 在 Windows 机器,即客户端上尝试使用 Putty 工具连接远程机器。

    图 4. SSH 连接成功

    到目前为止,我们已经可以正常的连接到这台远程机器。下面我们就要通过 Java 代码的方式来实现我们自己的这个远程登录的操作。

    验证 service name

    在 SSH 协议中定义了一些消息代码,而 50 至 79 这些代码是保留给用户认证协议层使用的,而 80 以上的数字是用于协议运行的,所以如果在用户认证协议验证之前,如果我们得到的消息代码是这个范围的,SSH 会返回错误信息,并断开连接。例如如以下几种消息所对应的代码号:

    SSH_MSG_USERAUTH_REQUEST 50:用户发送一个验证请求。

    SSH_MSG_USERAUTH_FAILURE 51:用户验证请求失败。

    SSH_MSG_USERAUTH_SUCCESS 52:用户验证请求成功。

    那么对于不同的认证方式,又有其各自的消息代码。

    在每次客户端发送请求的时候,服务器都会检查当前的 service name 和 username 是否合法,如果当前的 service name 或者 username 不可用,那么服务器端会立刻断掉请求连接。

    下面来实现一个对 service name 验证的请求,发送数据格式如下:

    byte SSH_MSG_SERVICE_REQUEST

    string service name in US-ASCIII

    具体代码如下:

    清单 3. 类 AuthServiceRequest

    package com.my.test.ssh2.auth;
     import com.my.test.ssh2.common.ProcessTypes;
     public class AuthServiceRequest {
         private String serviceName;
         public AuthServiceRequest(String serviceName){
                this.serviceName = serviceName;
         }
         /**
           * 取得指定服务器名称的认证消息
           * @return request – 返回一条十六进制消息
           **/
           public byte [] getRequestMessage() {
                byte [] request;
                ProcessTypes type = new ProcessTypes();
                type.asByte(AuthConstant.SSH_MSG_SERVICE_REQUEST);
                type.asString(serviceName);
                request = type.getBytes();
                return request;
     }}

    转换后发送的消息如下:

    [5, 0, 0, 0, 12, 115, 115, 104, 45, 117, 115, 101, 114, 97, 117, 116, 104]

    然后再对此进行算法加密,发送到服务器端。

    当前协议使用的 service name 是”ssh-userauth”,如果客户端请求的不是这个 service name,那么服务器会报如下错误:

    清单 4. service name 异常

    Caused by: java.io.IOException: Peer sent DISCONNECT message (reason code 2):
     bad service request demo-ssh-auth

    如果客户端通过了 service name 的验证后,下一步我们就可以实现具体的认证方式了。流程图如下所示:

    图 5. Authentication 类图

    TransportManager 类是用来处理传输协议层的业务逻辑。在这里主要处理数据的解密、加密、压缩等操作,这些功能的具体实现主要通过 TransportControl 类来完成,TrasportControl 类会根据客户端和服务器端协商的数据算法来选择具体的算法如 sha-1、MD5 等。通过 TransportControl 类处理完的数据会存储到 Packets 类里,生成一个数据包的列表,为 AuthManager 类提供必要的数据信息。Connect 类是用来处理连接协议层的业务逻辑。主要用于得到一个远程机器的连接句柄、产生一个安全信道、对 TransportManager 类做数据初始化操作等。AuthManager 类是用来处理认证协议层的业务逻辑。主要是对不同登录认证方式的请求和从服务器端得到的请求回复做处理,通过客户端选择的不同的认证方式调用不同的认证方式实现类,比如 AuthRequestByPassword 类定义了通过密码认证方式的实现。

    图 6. 认证协议流程图

    首先,开启一个线程用来接收从服务器发送的加密数据包,然后对这个数据包做算法解密处理并放到一个模型化的堆栈 (Packet List),而另一个线程会监听当前的 Packet List 里是否有可用的数据包,并对其做解析处理包括对数据包是否合法、是否满足某种认证算法等。如果数据包所包含的认证方式和当前客户端请求的认证方式不匹配,那么,客户端就会失去服务器的连接。反之,如果客户端请求的认证方式包含在服务器开启的认证方式,客户端会返回给服务器一个成功请求,并建立连接会话。

    none 认证方式

    无认证方式(none authentication),这种认证方式通常是在第一次请求发送的时候使用的,因为通过这个认证方式,我们可以得到当前服务器端支持的所有认证方式的列表,通过这个列表我们就可以验证我们想要使用的认证方式是否被服务器端所支持。当然,如果远程目标机器支持这种 none 认证方式,那么客户端就直接得到了一个会话连接,但是这种认证方式是 SSH 协议里所不推荐使用的。

    实现代码如下:

    清单 5. 类 AuthRequestByNone

    package com.my.test.ssh2.auth; 
     import com.my.test.ssh2.common.ProcessTypes; 
      public class AuthRequestByNone {    
           private String userName;    
           private String serviceName;    
           public AuthRequestByNone(String serviceName, String user) {        
                  this.serviceName = serviceName;        
                  this.userName = user;    
           } 
    
        /** 
         * 取得指定服务器名称和用户名的认证消息
         * @return request - 返回一条十六进制消息
         * */    
         public byte [] getRequestMessage() {        
             byte [] request;        
             ProcessTypes type = new ProcessTypes();        
             type.asByte(AuthConstant.SSH_MSG_USERAUTH_REQUEST);        
             type.asString(userName);        
             type.asString(serviceName);        
             type.asString(AuthConstant.SSH_NONE_AUTHENTICATION_METHOD);        
             request = type.getBytes();        
             return request;    
        } 
      }

     

    从流程图 6 中可以看出,当我们发送一个 none 认证方式的时候,如果服务器不支持 none 认证,那么客户端就可以取得服务器端的认证方式列表。首先解析服务器端返回的包信息,例如:

    [51, 0, 0, 0, 34, 112, 117, 98, 108, 105, 99, 107, 101, 121, 44, 103, 115, 115, 97, 112, 105, 45, 119, 105, 116, 104, 45, 109, 105, 99, 44, 112, 97, 115, 115, 119, 111, 114, 100, 0]

    经过客户端的算法解析之后,我们可以得到含有如下信息的数据包:( 对于算法原理的具体说明是定义在传输层协议里,不是本文所讨论的范围。)

    代码 51,说明用户验证请求失败。

    从第 5 位到第 34 位记录了所当前服务器所支持的认证方法,解析后可得到 publickey, gssapi-with-mic, password 的一个由逗号隔开的认证方式字符串。

    最后一位 0 表示,当前的请求失败,但并不是说整个的连接就断掉了。

    解析数据包的具体算法如下:

    清单 6. 解析数据算法

    ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) |
     ((arr[pos++] & 0xff) << 8) | (arr[pos++] & 0xff);

    对于服务器端来说,它要对客户端的请求作一反馈,从而说明当前的请求是否成功。
    数据格式如下:

    byte SSH_MSG_USERAUTH_FAILURE
     name-list authentications that can continue
     boolean partial success

     
    所以,这就正好解释了上述,从服务器端得到的数据解析结果。
    实现部分代码如下:
    清单 7. 初始化函数

    public boolean initialize(String userName) throws IOException{
       // 预处理服务名称的请求
     AuthServiceRequest serviceRequest = new
                 AuthServiceRequest(AuthConstant.SSH_SERVICE_NAME);
     IManager transManager = ManagerFactory.getManager(Constant.TRANSPORT_LAYER);
     transManager.sendMessage(serviceRequest.getRequestMessage());
       // 处理无认证方式的消息请求
       AuthRequestByNone authNone = new
                AuthRequestByNone(AuthConstant.SSH_CONN_SERVICE_NAME,userName);
       transManager.sendMessage(authNone.getRequestMessage());
        byte[] message = getMessage();
        // 验证当前的服务名称是否合法
        if(!isAccepted(message)){
            return false;
        }
        // 取得无认证方式的请求数据包
        message = getMessage();
        // 验证当前的请求是否成功
         if(isRequestFailed(message)){
            return false;
         }
        return true;
     }
     private boolean isRequestFailed(byte [] messages) throws IOException {
         if (messages[0] == AuthConstant.SSH_MSG_USERAUTH_SUCCESS){
            return true;
         }
         if (messages[0] == AuthConstant.SSH_MSG_USERAUTH_FAILURE){
             AuthFailure failure = new AuthFailure(messages);
             authentications = failure.getAuthThatCanContinue();
             isPartialSuccess = failure.isPartialSuccess();
             return false;
         }
         throw new IOException("Unexpected SSH message (type " + messages[0] + ")");
     }

    当客户端得到了这个 authentications 数组之后,客户端就可以验证当前用户使用的远程登录认证方式是否是服务器所支持的。如果是那么再发送一条匹配的认证方式,从而返回登录认证成功,否则失败并打印出合理的错误信息。下面用 password 的认证方式为例做进一步说明。

    password 认证方式

    对于 password 认证方式来说,它的数据请求格式如下:

    byte SSH_MSG_USERAUTH_REQUEST 
     string user name 
     string service name 
     string "password"
     boolean FALSE 
     string plaintext password in ISO-10646 UTF-8 encoding

    具体类的实现方式和 none 认证类的实现类似,只是 getRequestMessage() 方法所有不同。
    实现代码:
    清单 8. 生成请求数据函数

    public byte [] getRequestMessage() {
         byte [] request;
         ProcessTypes type = new ProcessTypes();
         type.asByte(AuthConstant.SSH_MSG_USERAUTH_REQUEST);
         type.asString(userName);
         type.asString(serviceName);
         type.asString(AuthConstant.SSH_PASSWORD_AUTHENTICATION_METHOD);
         type.asString(password);
         request = type.getBytes();
        return request;
     }

    在这里我们需要提供给服务器端一个用户口令,这个口令会在发送给服务器端之前被进行算法加密的处理。调用 password 认证方式的代码如下:

    清单 9. password 认证函数

     public boolean passwordAuthentication(String user, String pass) throws IOException{
         // 初始化请求
         initialize(user);
         // 验证指定的认证方式是否是 SSH 服务器所支持的
         if(verifyAuthenticatonMethods(AuthConstant.SSH_PASSWORD_AUTHENTICATION_METHOD)){
               return false;
         }
         // 调用密码认证方式
          AuthRequestByPassword passwordRequest = new
               AuthRequestByPassword(AuthConstant.SSH_CONN_SERVICE_NAME,user,pass);
         // 发送一个消息请求到服务器端
         IManager transManager = ManagerFactory.getManager(Constant.TRANSPORT_LAYER);
         transManager.sendMessage(passwordRequest.getRequestMessage());
         // 从服务器端获取数据包
          byte[] message = getMessage();
         // 验证当前的请求是否成功
         if(isRequestFailed(message)){
             return false;
         }
         return true;
     }

    客户端首先会做初始化操作,包括数据加密算法的协商、得到服务器端支持的认证方式等。其次客户端会检查当前用户使用的登录认证方式是否合法,然后再发送一个请求给服务器端,告诉服务器当前使用 password 认证进行远程登录。最后,服务器会返回一个数据包,里面包含了对这个请求的回复,如果验证成功,那么连接就可以开启一个安全的会话了。至此,password 认证方式的解析就完成了,接下来用户就可以对远程机器做操作了,这部分的具体说明是在 SSH 的连接层协议里,不是本文的讨论范围。


    展开全文
  • 参考网址:就特么忘记参考的那个网址了,好长时间之前写的pom.xml添加依赖com.jcraftjsch0.1.53org.apache.commonscommons-io1.3.2ssh连接测试代码:直接在main方法中调用即可//查看文件个数String commond = "ls -...
  • java实现ssh协议远程通过中间服务器连接跳转第三方服务器,目前只能连接到中间服务器 ,但是连接第三方服务器怎么实现 求解
  • java远程连接ssh实现

    万次阅读 2019-02-25 11:51:47
    在开发或者测试的过程中,经常会登录到远程服务器上... 这里给大家介绍一种基于java的远程ssh连接实现工具,jsch。jsch作为一种ssh2的纯java实现,能够允许我们连接到一个sshd 服务器,进而进行文件传输、文件下载...
  • 通过JCraft的JSch的依赖包(jsch-0.1.42.jar下载)实现SSH2协议远程通信,如远程连接到linux服务器进行文件上传下载或数据同步,具体代码示例如下所示importjava.io.File;@b@importjava.io.FileInputStream;@b@import...
  • SSH,也称为安全外壳或安全套接字外壳,是一种网络协议,允许一台计算机通过不安全的网络安全地连接到另一台计算机。在本教程中,我们将展示如何使用JSch和Apache MINA SSHD库通过Java与远程SSH服务器建立连接。 在...
  • 业务背景:公司想要实现Java代码中操作Linux命令,在页面上点击按钮,选择几个参数就可以操作集群,并且记录日志 想要实现这个功能有几个点需要关注 一.Java代码怎么连接远程的服务器? 大家可能都有一个想法,把...
  • 那么就可以遍历文件目录,实现文件或者文件夹得上传。话不多说,直接代码。 一:主类 import java.util.Properties; import com.cloudpower.util.Login; import com.util.LoadProperties; public class Ftp { p
  • (1)作为java的web后台应用,在做ssh连接的时候,比如需要导入所需要的协议jar包的,如ssh.jar,本次测试,本人使用的是。 (2) 导入jar包后,开始进入代码编程,首先需要进行创建用户名、密码、端口、ip地址等...
  • java实现web ssh客户端

    2020-12-31 21:59:24
    使用java语言实现web ssh客户端,使用websocket的stomp协议+xterm.js实现 效果如下: 0、引入依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-...
  • 这篇文章主要介绍了基于Java实现ssh命令登录主机执行shell命令过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下1、SSH命令SSH 为 Secure Shell 的缩写,...
  • 一、前言上一篇中我们已经知道了客户端通过socket来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户端呢?二、发送中文信息在python3中,socket只能发送bytes类型的数据,...
  • JSch特性介绍:http://www.jcraft.com/jsch/JSch组件下载:http://sourceforge.net/projects/jsch/files/jsch/0.1.53/jsch-0.1.53.zip/downloadJSch案例地址:http://www.jcraft.com/jsch/examples/实现说明以及实现...
  • java通过ssh连接服务器执行shell命令详解java通过ssh连接服务器执行shell命令:JSch 是SSH2的一个纯Java实现。它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己...
  • 这篇文章主要介绍了java通过ssh连接服务器执行shell命令详解及实例方法的相关资料java通过ssh连接服务器执行shell命令详解java通过ssh连接服务器执行shell命令:JSch 是SSH2的一个纯Java实现。它允许你连接到一个...
  • java连接远程主机的方式有多种,这里和大家分享的是通过ssh方式连接远程主机,使用的是jsch这个jar包,资源在这里。不懂ssh为何物的朋友可以从网上找找相关资料,这里只简单的解释下:SSH 为 Secure Shell(安全外壳...
  • Java通过SSH2协议执行远程Shell脚本(ganymed-ssh2-build210.jar)ganymed-ssh2简介:Ganymed SSH-2 for Java是用纯Java实现SSH-2协议的一个包。在使用它的过程中非常容易,只需要指定合法的用户名口令,或者授权认证...
  • 近来应需求需要,做了服务器之间的文件传输,利用的ssh协议。查阅各种资料博客,基本都是下载文件的方法,找不到下载文件夹得方法。思索多日写了一个下载文件夹的简单方法,可能步骤繁琐,优化不大好。由于jsch不能...
  • 1.参阅文档http://blog.csdn.net/pengchang_1981/article/details/8095683(http://blog.csdn.net/pengchang_1981/article/details/8095683)package com.hls.ssh.TestSSH;import java.io.BufferedReader;import java....
  • 先看看telnet 和ssh的定义:Telnet协议是TCP/IP协议中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到...
  • SSH-2实现java连接远程服务器并执行脚本命令

    万次阅读 多人点赞 2018-07-04 17:46:50
    参考文档: maven jar包:https://mvnrepository.com/artifact/ch.ethz.ganymed/ganymed-ssh2 Ganymed SSH2 API文档 :http://www.ganymed.ethz.ch/ssh2/javadoc/overview-summary.html ...Java的Ganymed SS...
  • 服务器ssh升级导致的java程序ssh连接失败解决办法java环境常用的ssh连接工具包有三...ssh连接问题是由于主机ssh升级后,服务器ssh协议中减少了一些加密算法,缺少与ssh工具包匹配的加密算法导致连接失败。暴力解决方案
  • java通过ssh连接服务器执行shell命令

    万次阅读 2017-01-11 23:20:08
    JSch 是SSH2的一个纯Java实现。它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己的 程序中。同时该项目也提供一个J2ME版本用来在手机上直连SSHD服

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,676
精华内容 9,870
关键字:

java实现ssh协议连接

java 订阅