Java實踐 — SSH遠程執行Shell腳本

1. SSH簡介

        SSH是Secure Shell的縮寫,一種創建在應用層和傳輸層基礎上的安全協議。SSH在鏈接和傳送過程當中會加密全部數據,能夠用來在不一樣系統或者服務器之間進行安全鏈接。SSH提供兩種的安全驗證方式:基於密碼的認證和基於密匙的認證。其中,基於密碼的認證比較簡單,只要知道遠程主機的用戶名和密碼,就能夠進行登陸。基於密匙的認證比較麻煩,並且鏈接比較耗時,這裏不詳細介紹。
        有不少基於SSH協議的客戶端,例如: PuTTYOpenSSH、Xshell 4等,能夠遠程鏈接幾乎全部UNIX平臺。同時,能夠經過Linux命令行ssh uername@host鏈接到某主機。
        在項目中,如何利用代碼實現SSH,遠程執行Shell腳本呢?JSch是Java Secure Channel的縮寫,是一個SSH2功能的純Java實現,具體信息能夠參考 JSch官網。它容許你鏈接到一個SSH服務器,而且可使用端口轉發,X11轉發,文件傳輸等,同時你也能夠集成它的功能到你本身的應用程序。在使用前,須要下載並導入JSch包: jsch-0.1.50.jar
 

2. 實現原理

        1. 根據遠程主機的IP地址,用戶名和端口,創建會話(Session);
        2. 設置用戶信息(包括密碼和Userinfo),而後鏈接session;
        3. 在session上創建指定類型的通道(Channel),本文示例中採用ChannelExec類型的;
        4. 設置channel上須要遠程執行的Shell腳本,鏈接channel,就能夠遠程執行該Shell腳本;
        5. 能夠讀取遠程執行Shell腳本的輸出,而後依次斷開channel和session的鏈接。
 

3. 示例代碼及分析

  • SSHCommandExecutor.java:
        import java.io.BufferedReader;  
        import java.io.InputStreamReader;  
        import java.util.Vector;  
          
        import com.jcraft.jsch.Channel;  
        import com.jcraft.jsch.ChannelExec;  
        import com.jcraft.jsch.JSch;  
        import com.jcraft.jsch.JSchException;  
        import com.jcraft.jsch.Session;  
          
        /** 
         * This class provide interface to execute command on remote Linux. 
         */  
          
        public class SSHCommandExecutor {  
            private String ipAddress;  
          
            private String username;  
          
            private String password;  
          
            public static final int DEFAULT_SSH_PORT = 22;  
          
            private Vector<String> stdout;  
          
            public SSHCommandExecutor(final String ipAddress, final String username, final String password) {  
                this.ipAddress = ipAddress;  
                this.username = username;  
                this.password = password;  
                stdout = new Vector<String>();  
            }  
          
            public int execute(final String command) {  
                int returnCode = 0;  
                JSch jsch = new JSch();  
                MyUserInfo userInfo = new MyUserInfo();  
          
                try {  
                    // Create and connect session.  
                    Session session = jsch.getSession(username, ipAddress, DEFAULT_SSH_PORT);  
                    session.setPassword(password);  
                    session.setUserInfo(userInfo);  
                    session.connect();  
          
                    // Create and connect channel.  
                    Channel channel = session.openChannel("exec");  
                    ((ChannelExec) channel).setCommand(command);  
          
                    channel.setInputStream(null);  
                    BufferedReader input = new BufferedReader(new InputStreamReader(channel  
                            .getInputStream()));  
          
                    channel.connect();  
                    System.out.println("The remote command is: " + command);  
          
                    // Get the output of remote command.  
                    String line;  
                    while ((line = input.readLine()) != null) {  
                        stdout.add(line);  
                    }  
                    input.close();  
          
                    // Get the return code only after the channel is closed.  
                    if (channel.isClosed()) {  
                        returnCode = channel.getExitStatus();  
                    }  
          
                    // Disconnect the channel and session.  
                    channel.disconnect();  
                    session.disconnect();  
                } catch (JSchException e) {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
                return returnCode;  
            }  
          
            public Vector<String> getStandardOutput() {  
                return stdout;  
            }  
          
            public static void main(final String [] args) {  
                SSHCommandExecutor sshExecutor = new SSHCommandExecutor("xx.xx.xx.xx", "username", "password");  
                sshExecutor.execute("uname -s -r -v");  
                  
                Vector<String> stdout = sshExecutor.getStandardOutput();  
                for (String str : stdout) {  
                    System.out.println(str);  
                }  
            }  
        }  
        getSession()只是建立一個session,須要設置必要的認證信息以後,調用connect()才能創建鏈接。
            調用openChannel(String type) 能夠在session上打開指定類型的channel。該channel只是被初始化,使用前須要先調用connect()進行鏈接。
            Channel的類型能夠爲以下類型:
    • shell - ChannelShell 
    • exec - ChannelExec 
    • direct-tcpip - ChannelDirectTCPIP 
    • sftp - ChannelSftp 
    • subsystem - ChannelSubsystem
            其中,ChannelShell和ChannelExec比較相似,均可以做爲執行Shell腳本的Channel類型。它們有一個比較重要的區別:ChannelShell能夠看做是執行一個交互式的Shell,而ChannelExec是執行一個Shell腳本。
  • MyUserInfo:
        import com.jcraft.jsch.UserInfo;  
          
        /** 
         * This class provide interface to feedback information to the user. 
         */  
        public class MyUserInfo implements UserInfo {  
            private String password;  
          
            private String passphrase;  
          
            @Override  
            public String getPassphrase() {  
                System.out.println("MyUserInfo.getPassphrase()");  
                return null;  
            }  
          
            @Override  
            public String getPassword() {  
                System.out.println("MyUserInfo.getPassword()");  
                return null;  
            }  
          
            @Override  
            public boolean promptPassphrase(final String arg0) {  
                System.out.println("MyUserInfo.promptPassphrase()");  
                System.out.println(arg0);  
                return false;  
            }  
          
            @Override  
            public boolean promptPassword(final String arg0) {  
                System.out.println("MyUserInfo.promptPassword()");  
                System.out.println(arg0);  
                return false;  
            }  
          
            @Override  
            public boolean promptYesNo(final String arg0) {  
                System.out.println("MyUserInfo.promptYesNo()");  
                System.out.println(arg0);  
                if (arg0.contains("The authenticity of host")) {  
                    return true;  
                }  
                return false;  
            }  
          
            @Override  
            public void showMessage(final String arg0) {  
                System.out.println("MyUserInfo.showMessage()");  
            }  
        }  
            MyUserInfo實現了接口UserInfo,主要是爲得到運行執行的用戶信息提供接口。大部分實現方法中,沒有作實質性的工做,只是輸出一下trace信息,幫助判斷哪一個方法被執行過。
     

    4. 執行結果分析

            1. 若是不設置UserInfo,會拋出JSchException異常,提示找不到host:
    a1.jpg
     
            2. 若是MyUserInfo實現了接口UserInfo,可是隻是@Override一些函數,會出現以下錯誤:
    a2.jpg
     
                 雖然能夠找到host,可是會拒絕訪問host。發現全部@Override函數中,get方法返回的都是null,prompt方法返回的都是false。
     
            3. 爲了判斷這些Override的methods中,那些是有用的,在每一個method中加入trace信息。發現只有promptYesNo(final String)會被調用到,輸出以下圖所示:
    a3.jpg
     
            4. promptYesNo(final String)是向用戶提出一個yes或者no的問題,來決定是否容許鏈接遠程主機。這纔是決定鏈接是否成功的一個關鍵函數。若是返回值爲true,則容許鏈接;若是返回值爲false,則拒絕鏈接。最後正確鏈接後的輸出入下圖所示:
    a4.jpg
     

    Reference

            JSch官網 
            JSch API

來自:http://blog.csdn.net/jmyue/article/details/14003783java

相關文章
相關標籤/搜索