public abstract class Shell implements AutoCloseable{
abstract boolean executeCommands(String... commands);
abstract String getResponse();
public abstract void close();
}
class SSH2Shell extends Shell{
private static Logger log = LoggerFactory.getLogger(SSH2Shell.class);
private static final int DEFAULT_TIME_OUT = 1000;
private static final int CONNECT_TIME_OUT = 3000;
private static final int COMMAND_EXECUTION_SUCCESS_OPCODE = -2;
public static final String BACKSLASH_R = "\r";
public static final String BACKSLASH_N = "\n";
public static final String COLON_CHAR = ":";
public static String ENTER_CHARACTER = BACKSLASH_R;
private Session session;
private ChannelShell channel;
private Expect4j expect = null;
private JSch jsch;
private StringBuffer buffer ;
// Threaded session variables
private boolean closed = false;
// 正则匹配,用于处理服务器返回的结果
public static String[] linuxPromptRegEx = new String[] { "~]$", "~]#", "~#", "#", ":~#", "/$", ">" };
public static String[] errorMsg = new String[] { "could not acquire the config lock " };
/**
* 利用JSch包实现远程主机SHELL命令执行, 链接远程主机
*
* @param ip 主机IP
* @param user 主机登陆用户名
* @param psw 主机登陆密码
* @param port 主机ssh2登陆端口,如果取默认值,传-1
* @param privateKey 密钥文件路径
* @param passphrase 密钥的密码
*/
SSH2Shell(String ip, String user, String psw, int port, String privateKey, String passphrase) {
buffer = new StringBuffer();
connect(ip, user, psw, port, privateKey, passphrase);
}
/**
* 利用JSch包实现远程主机SHELL命令执行, 链接远程主机 获得Expect4j对象,该对用可以往SSH发送命令请求
*
* @param ip 主机IP
* @param user 主机登陆用户名
* @param psw 主机登陆密码
* @param port 主机ssh2登陆端口,如果取默认值,传-1
* @param privateKey 密钥文件路径
* @param passphrase 密钥的密码
*/
void connect(String ip, String user, String psw, int port, String privateKey, String passphrase) {
log.info("---------- connect ssh ----------");
try {
log.debug(String.format("Start logging to %s@%s:%s", user, ip, port));
jsch = new JSch();
addIdentity(privateKey, passphrase);
;
session(ip, user, psw, port);
expect();
log.debug(String.format("Logging to %s@%s:%s successfully!", user, ip, port));
} catch (Exception ex) {
expect = null;
log.error("Connect to " + ip + ":" + port + "failed,please check your username and password!");
}
}
/**
* 执行配置命令
*
* @param commands 要执行的命令,为字符数组
* @return 执行是否成功
*/
boolean executeCommands(String... commands) {
// 如果expect返回为0,说明登入没有成功
if (expect == null) {
return false;
}
if (log.isDebugEnabled()) {
log.debug("----------Running commands are listed as follows:----------");
log.debug(Arrays.toString(commands));
log.debug("----------End----------");
}
try {
List<Match> lstPattern = lstPattern();
boolean isSuccess = true;
for (String strCmd : commands) {
isSuccess = executeCommand(lstPattern, strCmd);
}
// 防止最后一个命令执行不了
isSuccess = !checkResult(expect.expect(lstPattern));
// 找不到错误信息标示成功
String response = buffer.toString().toLowerCase();
for (String msg : errorMsg) {
if (response.indexOf(msg) > -1) {
return false;
}
}
return isSuccess;
} catch (Exception ex) {
log.error(ex.getLocalizedMessage(), ex);
return false;
}
}
/**
* 关闭SSH远程连接
*/
protected void disconnect() {
if (expect != null) {
expect.close();
}
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
log.info("---------- disconnect ssh ----------");
}
@Override
public void close() {
if (!this.closed) {
disconnect();
this.closed = true;
}
}
/**
* 获取服务器返回的信息
*
* @return 服务端的执行结果
*/
public String getResponse() {
return buffer.toString();
}
private void expect() throws JSchException, IOException {
channel = (ChannelShell) session.openChannel("shell");
expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
channel.connect(CONNECT_TIME_OUT);
}
private void session(String ip, String user, String psw, int port) throws JSchException {
if (port <= 0) {
session = jsch.getSession(user, ip);
} else {
session = jsch.getSession(user, ip, port);
}
if (session == null) {
throw new JSchException("Jsch session is null");
}
session.setPassword(psw);
Hashtable<String, String> config = new Hashtable<String, String>();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
localUserInfo ui = new localUserInfo();
session.setUserInfo(ui);
session.connect();
}
/**
* 设置ssh 免密登陆
*
* @param jsch
* @param privateKey
* @param passphrase
* @throws JSchException
*/
private void addIdentity(String privateKey, String passphrase) throws JSchException {
if (StringUtils.isBlank(privateKey)) {
return ;
}
if (passphrase != null && "".equals(passphrase)) {
//设置带口令的密钥
jsch.addIdentity(privateKey, passphrase);
} else {
//设置不带口令的密钥
jsch.addIdentity(privateKey);
}
}
/**
* @return
* @throws MalformedPatternException
*/
private List<Match> lstPattern() throws MalformedPatternException {
List<Match> lstPattern = new ArrayList<Match>();
String[] regEx = linuxPromptRegEx;
if (regEx != null && regEx.length > 0) {
synchronized (regEx) {
// list of regx like, :>, />
// etc. it is possible
// command prompts of your
// remote machine
for (String regexElement : regEx) {
RegExpMatch mat = new RegExpMatch(regexElement, x -> {
buffer.append(x.getBuffer());
x.exp_continue();
});
lstPattern.add(mat);
}
lstPattern.add(new EofMatch(x -> {
}));
lstPattern.add(new TimeoutMatch(DEFAULT_TIME_OUT, x -> {
}));
}
}
return lstPattern;
}
// 检查执行是否成功
private boolean executeCommand(List<Match> objPattern, String strCommandPattern) {
try {
boolean isFailed = checkResult(expect.expect(objPattern));
if (!isFailed) {
expect.send(strCommandPattern);
expect.send("\r");
return true;
}
return false;
} catch (MalformedPatternException ex) {
return false;
} catch (Exception ex) {
return false;
}
}
// 检查执行返回的状态
private boolean checkResult(int intRetVal) {
if (intRetVal == COMMAND_EXECUTION_SUCCESS_OPCODE) {
return true;
}
return false;
}
// 登入SSH时的控制信息
// 设置不提示输入密码、不显示登入信息等
private static class localUserInfo implements UserInfo {
String passwd;
public String getPassword() {
return passwd;
}
public boolean promptYesNo(String str) {
return true;
}
public String getPassphrase() {
return null;
}
public boolean promptPassphrase(String message) {
return true;
}
public boolean promptPassword(String message) {
return true;
}
public void showMessage(String message) {
}
}
}
依赖
<dependency>
<groupId>com.github.cverges.expect4j</groupId>
<artifactId>expect4j</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>