Java 中的远程交互式 SSH 命令
在 Java 中实现交互式 SSH 命令。
引言
本文介绍了一种通过 SSH(安全外壳协议)连接到服务器并在 Java 中通过伪终端执行交互式命令的方法。
背景
在 Java 中实现通过 SSH(安全外壳协议)连接到服务器并执行命令的程序非常常见,但大多数程序只能执行“exec”命令(非交互式命令,换句话说,执行命令并在命令完成后将控制权交出,例如“date”显示当前时间,而无法执行“ls”显示指定目录(opt/3com/VCX/vcxdata/)下的文件列表)。本文将展示一种执行这些命令的方法,并且进一步地,执行交互式命令(例如“su root”,它会请求密码,我们将根据收到的输出实时输入密码)。
Using the Code
首先,导入外部 jar 包 sshtools。然后,声明 SshClient
和 SessionChannelClient
的实例。您可能注意到获取会话输出的方式略有不同。我们没有声明 ChannelOutputStream
对象;而是声明了 SessionOutputReader
对象,我稍后会对此进行说明。
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
import com.sshtools.j2ssh.session.SessionChannelClient;
import com.sshtools.j2ssh.session.SessionOutputReader;
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification;
由于我们要通过 SSH 登录到 3com VCX 服务器,因此需要服务器 IP、用户名和密码,并按如下所示设置:
final private String username = "root";
final private String password = "pvadmin";
final private String hostname = "172.31.50.102";
private int outputPos = 0 ;
执行命令后,我们将输出作为字符串获取。先前的输出将包含在当前字符串中,以避免输出被重复显示。我们将 outputPos
设置为输出将显示的偏移量。
ssh = new SshClient();
try{
ssh.connect(hostname, new IgnoreHostKeyVerification());
创建一个 SSH 客户端通过 SSH 连接到服务器,并忽略主机密钥验证。
PasswordAuthenticationClient pwd = new PasswordAuthenticationClient();
pwd.setUsername(username);
pwd.setPassword(password);
int result = ssh.authenticate(pwd);
结果必须为 4;否则,意味着 SSH 连接没有正确设置。
if (result == 4)
{
logCat.info("Connected to host");
session = ssh.openSessionChannel();
sor = new SessionOutputReader(session);
成功连接到服务器后,打开会话通道,并创建会话的会话输出读取器实例。
if (session.requestPseudoTerminal("gogrid",80,24, 0 , 0, ""))
创建一个伪终端连接到服务器。终端类型可以是 dumb、xterm、gogrid、vt100、vt220、vt320 等,但我强烈建议选择“gogrid”,因为其他终端类型会导致混乱的字符。
这是请求的伪终端的原型:
boolean requestPseudoTerminal(String term, int cols,
int rows, int width, int height, String modes)
if (session.startShell())
{
in = (session.getInputStream());
logCat.info(in);
out = session.getOutputStream();
out.write("cd opt/3com/vcx/vcxdata/\n".getBytes());
out.write("ls\n".getBytes());
如果成功启动 shell,则执行以下命令:“cd /opt/3com/vcx/vcxdata/”。然后,“ls”显示该目录下的文件列表。
Thread.currentThread().sleep(1000*2);
String answer = null;
String aux = null;
aux = sor.getOutput();
answer = aux.substring(outputPos);
outputPos = aux.length();
logCat.info(answer);
执行命令后,检查输出。
类似地,我们可以根据服务器的最新输出提示实时执行交互式命令。例如,我们获得了 cworks 权限,然后我们想使用“su root”命令返回到 root 权限,密码提示将被请求。我们检查输出,并在获得密码提示时输入密码。
out.write("su cworks\n".getBytes()); // input command su cworks
out.write("su root\n".getBytes());
// then input command su root which will claim password
Thread.currentThread().sleep(1000*2);
//verify what we have got
String answer = null;
String aux = null;
aux = sor.getOutput(); // get the output in real time
answer = aux.substring(outputPos);
outputPos = aux.length();
logCat.info(answer);
String str[] = answer.split("\n");
String prompt = str[str.length-1]; // get the last prompt
prompt = prompt.trim();
if (prompt.contains("Password:"))
// if the last prompt is "password
{
// then we input password.
out.write("pvadmin\n".getBytes());
}
再次检查我们得到的内容:
Thread.currentThread().sleep(1000*2);
aux = sor.getOutput();
answer = aux.substring(outputPos);
logCat.info(answer);
感谢 SessionOutputReader
,我们可以实时读取会话的输出流。因此,我们可以根据服务器的输出提示决定应该输入什么命令,这正是交互性的本质。
关注点
如果这对您有所帮助,我将不胜感激。感谢您的建议,这帮助我改进了这篇文章。
参考:http://upcommons.upc.edu/pfc/bitstream/2099.1/3790/1/53848-1.pdf。