java 通過 ssh 執行 shell 命令
阿新 • • 發佈:2018-12-09
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 %[email protected]%s:%s", user, ip, port));
jsch = new JSch();
addIdentity(privateKey, passphrase);
;
session(ip, user, psw, port);
expect();
log.debug(String.format("Logging to %[email protected]%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>