jsch教程

  jsch 是ssh2的一个纯Java实现。它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己的 程序中。同时该项目也提供一个J2ME版本用来在手机上直连SSHD服务器


一般连接到服务器有两种方式:

1、通过用户名和密码连接,缺点(出于安全需要,一般服务器的密码会定期修改,程序部署后将不得不经常更新配置文件中的密    码。)

2、通过用户名和ssh private key file连接,缺点(因为Java程序必须和private key file在同一台机器上,将服务器的private key file复制到本地后,本地机器的安全措施可能会使private key file被窃取,威胁服务器安全。)


  jsch官网地址为http://www.jcraft.com/jsch/,实现jsch功能需要添加一个jsch-0.1.51.jar包,官网有一些例子可直接下载参考。

maven的配置为:


            com.jcraft
            jsch
            0.1.51



1、jsch连接到linux的基本原理和用ssh一样,需要ip地址,端口(一般为22),用户名,密码,为了方便配置,可以把静态变量初始化在配置文件中:

 

#jsch配置
host.ip=192.168.1.1
host.user=root
host.port=22
host.password=123456
host.connect.timeout=5000	

获取配置文件变量的工具类:

package com.personal.core.constant;


import com.personal.core.utils.PropertiesLoader;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.DefaultResourceLoader;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 注释:全局配置类
 *
 * @Author: coding99
 * @Date: 16-9-2
 * @Time: 下午7:33
 */
public class Global {


	/**
	 * 当前对象实例
	 */
	private static Global global = new Global();
	
	/**
	 * 保存全局属性值
	 */
	private static Map map = new HashMap();
	
	/**
	 * 属性文件加载对象
	 */
	private static PropertiesLoader loader = new PropertiesLoader("sysConfig.properties");


	
	/**
	 * 获取当前对象实例
	 */
	public static Global getInstance() {
		return global;
	}
	
	/**
	 * 获取配置
	 * @see {fns:getConfig('adminPath')}
	 */
	public static String getConfig(String key) {
		String value = map.get(key);
		if (value == null){
			value = loader.getProperty(key);
			map.put(key, value != null ? value : StringUtils.EMPTY);
		}
		return value;
	}/**     * 获取工程路径     * @return     */    public static String getProjectPath(){    	// 如果配置了工程路径,则直接返回,否则自动获取。String projectPath = Global.getConfig("projectPath");if (StringUtils.isNotBlank(projectPath)){return projectPath;}try {File file = new DefaultResourceLoader().getResource("").getFile();if (file != null){while(true){File f = new File(file.getPath() + File.separator + "src" + File.separator + "main");if (f == null || f.exists()){break;}if (file.getParentFile() != null){file = file.getParentFile();}else{break;}}projectPath = file.toString();}} catch (IOException e) {e.printStackTrace();}return projectPath;    }}
	
    /**
     * 获取工程路径
     * @return
     */
    public static String getProjectPath(){
    	// 如果配置了工程路径,则直接返回,否则自动获取。
		String projectPath = Global.getConfig("projectPath");
		if (StringUtils.isNotBlank(projectPath)){
			return projectPath;
		}
		try {
			File file = new DefaultResourceLoader().getResource("").getFile();
			if (file != null){
				while(true){
					File f = new File(file.getPath() + File.separator + "src" + File.separator + "main");
					if (f == null || f.exists()){
						break;
					}
					if (file.getParentFile() != null){
						file = file.getParentFile();
					}else{
						break;
					}
				}
				projectPath = file.toString();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return projectPath;
    }


}



2、为了方便脚本配置,统一管理,修改,我们可以把脚本放在一个xml里面,例如:




    
    
        查看opt文件夹内容
        ls -ltr /opt
    

    
    
        查看opt文件夹内容
        ls -ltr /opt
    

    
    
        查看opt文件夹内容
        ls -ltr /opt
    




3、获取脚本的方法通过一个工具类读取这个配置文件,用dom4j进行解析,获取相应指定shell name的脚本,例如

package com.personal.core.utils;

import com.personal.core.constant.Global;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.List;

/**
 * 注释
 *
 * @Author: coding99
 * @Date: 16-9-2
 * @Time: 下午9:31
 */
public class ShellConfigUtil {

    private static final String SHELL_ENV = Global.getConfig("environment");
    private static final String DEFAULT_SHELL_CONFIG = ShellConfigUtil.class.getResource("/").getPath()+"shell.xml";

    private ShellConfigUtil() {

    }


    /**
     * 取得脚本
     * @param shellName
     * @return
     * @throws Exception
     */
    public static String getShell(String shellName)throws Exception{
        String shellContent = "";

        List shells = getShellEelements();

        for(Element shell : shells) {
            String name = shell.attributeValue("name");
            String env = shell.attributeValue("env");
            //把shell数组遍历
            if(shellName.equals(name) && SHELL_ENV.equals(env)) {
                shellContent = getShellResult(shell);
            }

        }
        return shellContent;
    }

    /**
     * 解析xml
     * @return
     * @throws Exception
     */
    public static List getShellEelements() throws Exception{
        SAXReader saxReader = new SAXReader();//创建读取配置文件的对象
        Document document = saxReader.read(new File(DEFAULT_SHELL_CONFIG));//开始读取配置文件
        Element element = document.getRootElement();
        List shellElements = element.elements("shell");
        return shellElements;
    }

    /**
     * 获取脚本内容
     * @param shell
     * @return
     */
    public static String getShellResult(Element shell) {
        String result = "";
        List steps = shell.elements("step");
        for (Element step : steps) {
            result = step.getText();
        }
        return result;
    }



    public static void main(String[] args) throws Exception{
        ShellConfigUtil.getShell("ls");
    }

}

 
  

4、JSCH实现原理:

jsch进行连接服务器连接时可以看做时java的jdbc连接,首先我们需要实例化一个jsch对象,再利用这个对象 根据用户名,主机ip,端口获取一个Session对象,设置好相应的参数后,就进行连接,创建连接后这个session是一直可用的,所以不需要关闭。之后我们需要在session上建立channel通道

Channel的类型可以为如下类型:

shell - ChannelShell exec - ChannelExec  

direct-tcpip - ChannelDirectTCPIP sftp - ChannelSftp subsystem - ChannelSubsystem

 其中,ChannelShell和ChannelExec比较类似,都可以作为执行Shell脚本的Channel类型。它们有一个比较重要的区别:ChannelShell可以看作是执行一个交互式的Shell,而ChannelExec是执行一个Shell脚本。

实现远程命令操作我们需要创建ChannelExec对象。

实现文件上传下载我们需要实现ChannelSftp对象。

ChannelSftp类是JSch实现SFTP核心类,它包含了所有SFTP的方法,如:
put():      文件上传
get():      文件下载
cd():       进入指定目录
ls():       得到指定目录下的文件列表
rename():   重命名指定文件或目录
rm():       删除指定文件
mkdir():    创建目录
rmdir():    删除目录下面有个工具类展示
应用实例:
JschUtil.java
package com.personal.core.utils;
import com.jcraft.jsch.*;
import com.personal.core.constant.Global;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
/**
 * 注释
 *
 * @Author: coding99
 * @Date: 16-9-2
 * @Time: 下午7:33
 */
public class JschUtil {

    private static final Logger logger = LoggerFactory.getLogger(JschUtil.class);

    private String charset = "UTF-8"; // 设置编码格式,可以根据服务器的编码设置相应的编码格式
    private JSch jsch;
    private Session session;
    Channel channel = null;
    ChannelSftp chSftp = null;


    //初始化配置参数
    private String jschHost = Global.getConfig("host.ip");
    private int jschPort = Integer.parseInt(Global.getConfig("host.port"));
    private String jschUserName = Global.getConfig("host.user");
    private String jschPassWord = Global.getConfig("host.password");
    private int jschTimeOut = Integer.parseInt(Global.getConfig("host.connect.timeout"));



    /**
     * 静态内部类实现单例模式
     */
    private static class LazyHolder {
        private static final JschUtil INSTANCE = new JschUtil();
    }

    private JschUtil() {

    }

    /**
     * 获取实例
     * @return
     */
    public static final JschUtil getInstance() {
        return LazyHolder.INSTANCE;
    }


    /**
     * 连接到指定的服务器
     * @return
     * @throws JSchException
     */
    public boolean connect() throws JSchException {

        jsch = new JSch();// 创建JSch对象

        boolean result = false;

        try{

            long begin = System.currentTimeMillis();//连接前时间
            logger.debug("Try to connect to jschHost = " + jschHost + ",as jschUserName = " + jschUserName + ",as jschPort =  " + jschPort);

            session = jsch.getSession(jschUserName, jschHost, jschPort);// // 根据用户名,主机ip,端口获取一个Session对象
            session.setPassword(jschPassWord); // 设置密码
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);// 为Session对象设置properties
            session.setTimeout(jschTimeOut);//设置连接超时时间
            session.connect();

            logger.debug("Connected successfully to jschHost = " + jschHost + ",as jschUserName = " + jschUserName + ",as jschPort =  " + jschPort);

            long end = System.currentTimeMillis();//连接后时间

            logger.debug("Connected To SA Successful in {} ms", (end-begin));

            result = session.isConnected();

        }catch(Exception e){
            logger.error(e.getMessage(), e);
        }finally{
            if(result){
                logger.debug("connect success");
            }else{
                logger.debug("connect failure");
            }
        }

        if(!session.isConnected()) {
            logger.error("获取连接失败");
        }

        return  session.isConnected();

    }

    /**
     * 关闭连接
     */
    public void close() {

        if(channel != null && channel.isConnected()){
            channel.disconnect();
            channel=null;
        }

        if(session!=null && session.isConnected()){
            session.disconnect();
            session=null;
        }

    }

    /**
     * 脚本是同步执行的方式
     * 执行脚本命令
     * @param command
     * @return
     */
    public Map execCmmmand(String command) throws Exception{


        Map mapResult = new HashMap();

        logger.debug(command);

        StringBuffer result = new StringBuffer();//脚本返回结果

        BufferedReader reader = null;
        int returnCode = -2;//脚本执行退出状态码


        try {

            channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(command);
            channel.setInputStream(null);
            ((ChannelExec) channel).setErrStream(System.err);

            channel.connect();//执行命令 等待执行结束

            InputStream in = channel.getInputStream();
            reader = new BufferedReader(new InputStreamReader(in, Charset.forName(charset)));

            String res="";
            while((res=reader.readLine()) != null){
                result.append(res+"\n");
                logger.debug(res);
            }

            returnCode = channel.getExitStatus();

            mapResult.put("returnCode",returnCode);
            mapResult.put("result",result.toString());

        } catch (IOException e) {

            logger.error(e.getMessage(),e);

        } catch (JSchException e) {

            logger.error(e.getMessage(), e);

        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }

        return mapResult;

    }

    /**
     * 上传文件
     *
     * @param directory 上传的目录,有两种写法
     *                  1、如/opt,拿到则是默认文件名
     *                  2、/opt/文件名,则是另起一个名字
     * @param uploadFile 要上传的文件 如/opt/xxx.txt
     */
    public void upload(String directory, String uploadFile) {

        try {

            logger.debug("Opening Channel.");
            channel = session.openChannel("sftp"); // 打开SFTP通道
            channel.connect(); // 建立SFTP通道的连接
            chSftp = (ChannelSftp) channel;
            File file = new File(uploadFile);
            long fileSize = file.length();

            /*方法一*/
             OutputStream out = chSftp.put(directory, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式
             byte[] buff = new byte[1024 * 256]; // 设定每次传输的数据块大小为256KB
             int read;
             if (out != null) {
                 logger.debug("Start to read input stream");
                InputStream is = new FileInputStream(uploadFile);
                do {
                    read = is.read(buff, 0, buff.length);
                     if (read > 0) {
                            out.write(buff, 0, read);
                     }
                     out.flush();
                 } while (read >= 0);
                 logger.debug("input stream read done.");
             }

            // chSftp.put(uploadFile, directory, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); //方法二
            // chSftp.put(new FileInputStream(src), dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); //方法三
            logger.debug("成功上传文件至"+directory);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            chSftp.quit();

            if (channel != null) {
                channel.disconnect();
                logger.debug("channel disconnect");
            }

        }
    }


    /**
     * 下载文件
     *
     * @param directory 下载的目录,有两种写法
     *                  1、如/opt,拿到则是默认文件名
     *                  2、/opt/文件名,则是另起一个名字
     * @param downloadFile 要下载的文件 如/opt/xxx.txt
     *
     */

    public void download(String directory, String downloadFile) {
        try {

            logger.debug("Opening Channel.");
            channel = session.openChannel("sftp"); // 打开SFTP通道
            channel.connect(); // 建立SFTP通道的连接
            chSftp = (ChannelSftp) channel;
            SftpATTRS attr = chSftp.stat(downloadFile);
            long fileSize = attr.getSize();


            OutputStream out = new FileOutputStream(directory);

            InputStream is = chSftp.get(downloadFile, new MyProgressMonitor());
            byte[] buff = new byte[1024 * 2];
            int read;
            if (is != null) {
                logger.debug("Start to read input stream");
                do {
                    read = is.read(buff, 0, buff.length);
                    if (read > 0) {
                        out.write(buff, 0, read);
                    }
                    out.flush();
                } while (read >= 0);
                logger.debug("input stream read done.");
            }

            //chSftp.get(downloadFile, directory, new FileProgressMonitor(fileSize)); // 代码段1
            //chSftp.get(downloadFile, out, new FileProgressMonitor(fileSize)); // 代码段2

            logger.debug("成功下载文件至"+directory);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            chSftp.quit();
            if (channel != null) {
                channel.disconnect();
                logger.debug("channel disconnect");
            }
        }
    }


    /**
     * 删除文件
     * @param deleteFile 要删除的文件
     */
    public void delete(String deleteFile) {

        try {
            connect();//建立服务器连接
            logger.debug("Opening Channel.");
            channel = session.openChannel("sftp"); // 打开SFTP通道
            channel.connect(); // 建立SFTP通道的连接
            chSftp = (ChannelSftp) channel;
            chSftp.rm(deleteFile);
            logger.debug("成功删除文件"+deleteFile);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }



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

        JschUtil jschUtil = JschUtil.getInstance();

        boolean isConnected = false;
        isConnected  = jschUtil.connect();

        if(isConnected == true){


            /*上传文件*/
            jschUtil.upload("/opt/123456.png","/home/sky/Desktop/resizeApi.png");

             /*执行命令*/
            String command = "ls -ltr /opt";
           // String command = ShellConfigUtil.getShell("ls");
            Map result = jschUtil.execCmmmand(command);
            System.out.println(result.get("result").toString());

            /*下载文件*/
            jschUtil.download("/opt/123456.png","/opt/123456.png");

            jschUtil.close();

        }


    }
}






 
   
package com.personal.core.utils;
import java.text.DecimalFormat;
import java.util.Timer;
import java.util.TimerTask;

import com.jcraft.jsch.SftpProgressMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 注释
 *
 * @Author: coding99
 * @Date: 16-9-2
 * @Time: 下午8:29
 */
public class FileProgressMonitor extends TimerTask implements SftpProgressMonitor {

    private static final Logger logger = LoggerFactory.getLogger(FileProgressMonitor.class);
    

    private long progressInterval = 5 * 1000; // 默认间隔时间为5秒

    private boolean isEnd = false; // 记录传输是否结束

    private long transfered; // 记录已传输的数据总大小

    private long fileSize; // 记录文件总大小

    private Timer timer; // 定时器对象

    private boolean isScheduled = false; // 记录是否已启动timer记时器

    public FileProgressMonitor(long fileSize) {
        this.fileSize = fileSize;
    }
  
    @Override
    public void run() {

        if (!isEnd()) { // 判断传输是否已结束

            logger.debug("Transfering is in progress.");

            long transfered = getTransfered();

            if (transfered != fileSize) { // 判断当前已传输数据大小是否等于文件总大小
                logger.debug("Current transfered: " + transfered + " bytes");
                sendProgressMessage(transfered);
            } else {
                logger.debug("File transfering is done.");
                setEnd(true); // 如果当前已传输数据大小等于文件总大小,说明已完成,设置end
            }
        } else {
            logger.debug("Transfering done. Cancel timer.");
            stop(); // 如果传输结束,停止timer记时器
            return;
        }
    }

    public void stop() {
        logger.debug("Try to stop progress monitor.");
        if (timer != null) {
            timer.cancel();
            timer.purge();
            timer = null;
            isScheduled = false;
        }
        logger.debug("Progress monitor stoped.");
    }

    public void start() {
        logger.debug("Try to start progress monitor.");
        if (timer == null) {
            timer = new Timer();
        }
        timer.schedule(this, 1000, progressInterval);
        isScheduled = true;
        logger.debug("Progress monitor started.");
    }

    /**
     * 打印progress信息
     * @param transfered
     */
    private void sendProgressMessage(long transfered) {
        if (fileSize != 0) {
            double d = ((double)transfered * 100)/(double)fileSize;
            DecimalFormat df = new DecimalFormat( "#.##");
            logger.debug("Sending progress message: " + df.format(d) + "%");
        } else {
            logger.debug("Sending progress message: " + transfered);
        }
    }

    /**
     * 实现了SftpProgressMonitor接口的count方法
     */
    public boolean count(long count) {
        if (isEnd()) return false;
        if (!isScheduled) {
            start();
        }
        add(count);
        return true;
    }

    /**
     * 实现了SftpProgressMonitor接口的end方法
     */
    public void end() {
        setEnd(true);
        logger.debug("transfering end.");
    }

    private synchronized void add(long count) {
        transfered = transfered + count;
    }

    private synchronized long getTransfered() {
        return transfered;
    }

    public synchronized void setTransfered(long transfered) {
        this.transfered = transfered;
    }

    private synchronized void setEnd(boolean isEnd) {
        this.isEnd = isEnd;
    }

    private synchronized boolean isEnd() {
        return isEnd;
    }

    public void init(int op, String src, String dest, long max) {
        // Not used for putting InputStream
    }
}


 
   
package com.personal.core.utils;


import com.jcraft.jsch.SftpProgressMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;




/**
 * 注释
 *
 * @Author: coding99
 * @Date: 16-9-2
 * @Time: 下午8:36
 */


public class MyProgressMonitor implements SftpProgressMonitor {


    private static final Logger logger = LoggerFactory.getLogger(MyProgressMonitor.class);


    private long transfered;


    @Override
    public boolean count(long count) {
        transfered = transfered + count;
        logger.debug("Currently transferred total size: " + transfered + " bytes");
        return true;
    }


    @Override
    public void end() {
        logger.debug("Transferring done.");
    }


    @Override
    public void init(int op, String src, String dest, long max) {
        logger.debug("Transferring begin.");
    }


}
:工具类主要封装了三个方法 
1、执行脚本命令 
2、文件上传(为了做进度监控,需要用到监控类FileProgressMonitor.java) 
3、文件下载 (为了做进度监控,需要用到监控类MyProgressMonitor.java

JschUtil.java工具类的几个使用步骤如下
1、初始化连接参数
2、调用connect()方法进行连接
3、执行相应的方法,如果是脚本命令,先用ShellConfigUtil.java获取相应的脚本,再执行 (jsch执行脚本的方式是同步,就是要等到脚本执行结束才返回结果,如果没有返回就一直等着,应该不支持交互式命令,比如连接到某个数据库,然后执行相应的操作)
4、最后关闭连接


本例子只针对单台服务器的操作,如果需要同时操作多台服务器,可在原有基础上做修改。




 
   h需要elSftp 
   ChannelS 
   

你可能感兴趣的:(java)