当前位置: 首页 > 工具软件 > Maverick > 使用案例 >

maverick组件实现JAVA SSH协议初探

季俭
2023-12-01

        最近研究用SSH协议访问远程Linux机器,采用最普通的用户帐号和口令连接方式,端口为默认的22,以及需要执行的shell命令。我google了下用java 实现的SSH实现方式,找到了一款商业软件J2SSH Maverick(看网站介绍,该软件就是原来sourceforge上的J2SSH的升级版,更强大,更稳定,效率更高),网站只允许试用45天,过后需要购买Licenses,网址是http://www.javassh.com/products/j2ssh-maverick,用户可以在网页右面下载最新版或者稳定版,我下载试用的是1.4.35稳定版,下面可以输入个人邮箱,系统将发送试用代码的邮件到个人邮箱。闲话少述,下面进入代码正题。

       下载下来maverick.zip差不多有5M多,里面包含dist文件夹包含JAVA和J2ME版,JAVA里面包含6个JAR文件,docs里面是api文档,examples里面是相关的使用例子,相当具有参考价值,lib里面是依赖的JAR包,有4个,分别是bcprov.jar,commons-logging-java1.1.jar,jakarta-oro.jar,log4j-java1.1.jar,我使用是将这10个jar全部引入项目的classpath里面。下面就展示下操作的代码。

        在试用组件的类之前务必加上发到邮箱中的license代码才可,否则会报license失效的异常,连接并登陆服务器的代码如下:

       

/**
 	 * 登陆SSH服务器
 	 */
 	public void login() { 		
 		com.maverick.ssh.LicenseManager.addLicense("----BEGIN 3SP LICENSE----\r\n"
                 + "Product : J2SSH Maverick\r\n"
                 + "Licensee: 455607334@qq.com\r\n"
                 + "Comments: 211.137.58.245\r\n"
                 + "Type    : Evaluation License\r\n"
                 + "Created : 07-Sep-2012\r\n"
                 + "Expires : 22-Oct-2012\r\n"
                 + "\r\n"
                 + "37872030DA4FA3DBD9FF0D0AE6F5DF373E5A5888C7CE7E56\r\n"
                 + "2DD8E38195E850FB96BA7F23FF71DD3C002168D831DD6ED4\r\n"
                 + "29511478489E342D850338FC18622CBF1E28B1E1B4FF5FDA\r\n"
                 + "49206DC4D649EAFA8196E351A464ABF3ED10E3C3DBAFCAF3\r\n"
                 + "28D6A7F6B15DC340823F2994F6CED31CA47A8A61160AF684\r\n"
                 + "E8EE5489EDE6F3C550AF9BBEB186B456799BD54E107A1D70\r\n"
                 + "----END 3SP LICENSE----\r\n");
 
 		/**
 		 * Create an SshConnector instance
 		 */
 		try {
 			SshConnector con = SshConnector.getInstance();
 
 			SocketTransport transport = new SocketTransport(hostname, port);
 
 			/*
 			 * We must use buffered mode so that we have a background thread to
 			 * fire data events back to us.
 			 */
 			ssh = con.connect(transport, username, true);
 
 			/**
 			 * Determine the version
 			 */
 			if (ssh instanceof Ssh1Client)
 				logger.infoT(hostname + " is an SSH1 server");
 			else
 				logger.infoT(hostname + " is an SSH2 server");
 
 			PasswordAuthentication pwd = new PasswordAuthentication();
 
 			do {
 				logger.infoT("Password: ");
 				pwd.setPassword(password);
 			} while (ssh.authenticate(pwd) != SshAuthentication.COMPLETE
 					&& ssh.isConnected());
 
 		} catch (Throwable e) {
 			e.printStackTrace();
 		}
 
 	}

hostname,port,username,password分别是提供的ip地址,端口,用户和口令密码,
ssh = con.connect(transport, username, true);
返回的是SshClient对象,并可判断是基于SSH1还是SSH2协议。针对密码校验方式就需要实例化一个PasswordAuthentication对象,并setPassword口令。通过ssh.authenticate(pwd)进行校验,如果结果为SshAuthentication.COMPLETE表示口令和密码通过,则表明连接上了该远程服务器。

下段是发送执行命令的代码:

/**
 	 * 发送命令并执行,可执行以";"分隔的多个命令
 	 * 
 	 * @param command
 	 *            发送执行的命令
 	 * @return 返回命令的执行结果
 	 */
 	public String execCommand(final String command) {
 		final StringBuilder sb = new StringBuilder();
 		try {
 			// 如果通过验证
 			if (ssh.isAuthenticated()) {
 
 				ChannelAdapter eventListener = new ChannelAdapter() {
 					public void dataReceived(SshChannel channel, byte[] buf,
 							int offset, int len) {
 
 						sb.append(new String(buf, offset, len));
 					}
 
 					public synchronized void channelClosed(SshChannel channel) {
 						logger.infoT("notify!");
 						notifyAll();
 					}
 				};
 
 				logger.infoT("start exec command!");
 				final SshSession session = ssh
 						.openSessionChannel(eventListener);
 
 				PseudoTerminalModes pty = new PseudoTerminalModes(ssh);
 				pty.setTerminalMode(PseudoTerminalModes.ECHO, false);
 
 				if (session.requestPseudoTerminal("vt100", 80, 24, 0, 0, pty)) {
 
 					session.setAutoConsumeInput(true);
 					//
 					logger.infoT("requestPseudoTerminal success!");
 
 					if (session.startShell()) {
 
 						logger.infoT("startShell success!");
 
 						
 						Thread.sleep(4000);
 						Thread t = new Thread() {
 							public void run() {
 
 								try {
 									if (!session.isClosed()) {
 
 										
 										sb.delete(0, sb.length());
 
 										if (!"".equals(command)) {
 											if (command.contains(";")) {
 												String[] splitCommands = command
 														.split(";");
 												for (String str : splitCommands)
 													session.getOutputStream()
 															.write((str + "\n")
 																	.getBytes());
 											} else
 												session.getOutputStream()
 														.write((command + "\n")
 																.getBytes());
 										}
 
 									}
 								} catch (IOException ex) {
 									logger.exception(ex);
 								}
 							}
 						};
 
 						t.start();
 
 						// sleep 5 秒,等待跑完
 						Thread.sleep(5000);
 					} else
 						logger.infoT("Authentication failed");
 				}
 				logger.infoT("close session!");
 				session.close();
 			}
 
 		} catch (Throwable e) {
 			e.printStackTrace();
 			logger.exception(e);
 		}
 		return sb.toString();
 	}
         首先ssh.isAuthenticated()判断是否认证过,再利用ChannelAdapter作为SshSession的监听器,final SshSession session = ssh
 .openSessionChannel(eventListener);并ChannelAdapter实现dataReceived方法,其中buf,offset,len为命令发送后相当于从InputStreamRead中的read方法读取的数据。再加上PseudoTerminalModes模式,避免部分ssh协议的ECHO的影响,并通过session.requestPseudoTerminal开启一个名称为vt100,高为80,宽为24的虚拟终端,然后session.startShell()开启Shell模式。下面开启一个线程通过session.getOutputStream().write(command.getBytes()),输出相关命令,下面不能立刻session.close,必须主线程睡眠一段时间,我设置的是5秒,作用是避免流里的数据没读完就关闭了导致命令结果不完整。这里没研究出更好的办法能监听Channel中的数据读完。最后必须关闭session,最后使用完后需要ssh.disconnect();

         总结,该软件是个商业收费软件,只能试用45天,如果超过45天需要购买Licenses,最便宜的好像都要999$,另外个人这里只是实现了单命令执行的一种办法,对于多命令还有待研究,关于其它PublicKey,Proxy的ssh协议校验方式还有待进一步研究,欢迎各位JAVA高手指正。
                                                     



 类似资料: