我正在尝试编写一个可以ssh到unix服务器并重置用户密码的Java代码。我使用jsch进行连接和命令执行。我在几台服务器(CentOS,Ubuntu,HP-UX)上测试过,它工作正常。
当我在AIX上测试时,我遇到了这个问题,它提示用户的新密码。我的程序将一直挂起,直到发生超时。然后会出现错误。我确实谷歌如何阻止用户交互here,但遗憾的是,我正在处理的AIX没有chpasswd
。在搜索有关错误代码here的更多信息时,我发现了可用于此案例的expect4j库。
我尝试了一些像here这样的例子并将其应用到我的代码中。
这是我的代码:
public void executeSetPassword(final String userName, final GuardedString password) {
JSch.setLogger(new JSCHLogger());
if ((userName != null) && (password != null)) {
JSch jsch = new JSch();
String host = configuration.getHost();
String remoteUser = configuration.getRemoteUser();
GuardedString passwd = configuration.getPassword();
final Session session;
try {
session = jsch.getSession(remoteUser, host, 22);
passwd.access(new Accessor(){
@Override
public void access(char[] clearChars) {
session.setPassword(new String(clearChars));
}});
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
logger.info("sessionStatus is " + session.isConnected());
final ChannelExec channel=(ChannelExec) session.openChannel("exec");
password.access(new Accessor() {
@Override
public void access(char[] clearChars) {
channel.setCommand("echo -e " + "\"" + new String(clearChars) + "\\n" + new String(clearChars) + "\"" + " | passwd " + userName + ";pwdadm -c " + userName);
}});
channel.setErrStream(System.err);
channel.setPty(true);
final Expect4j expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
channel.connect();
final StringBuilder buffer = new StringBuilder();
Closure closure = new Closure() {
public void run(ExpectState expectState) throws Exception {
buffer.append(expectState.getBuffer());//string buffer for appending output of executed command
}
};
expect.expect("New password");
password.access(new Accessor() {
@Override
public void access(char[] clearChars) {
try {
expect.send(new String(clearChars));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}});
BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(channel.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
logger.info(line);
if( line.contains("New password:") == true) {
logger.info("set something here");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(channel.isClosed()) {
channel.disconnect();
session.disconnect();
expect.close();
logger.info("Exit status = " + channel.getExitStatus());
}
}
catch ( Exception e) {
throw new RuntimeException(e);
}
}
当我尝试运行它时,同样的事情发生了。这是否意味着我的期望无法正常工作?
这是日志:
INFO: Connecting to 192.168.1.39 port 22
INFO: Connection established
INFO: Remote version string: SSH-1.99-OpenSSH_3.8.1p1
INFO: Local version string: SSH-2.0-JSCH-0.1.53
INFO: CheckCiphers: aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256
INFO: aes256-ctr is not available.
INFO: aes192-ctr is not available.
INFO: aes256-cbc is not available.
INFO: aes192-cbc is not available.
INFO: CheckKexes: diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521
INFO: CheckSignatures: ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
INFO: SSH_MSG_KEXINIT sent
INFO: SSH_MSG_KEXINIT received
INFO: kex: server: diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1
INFO: kex: server: ssh-rsa,ssh-dss
INFO: kex: server: aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes128-ctr,aes192-ctr,aes256-ctr
INFO: kex: server: aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes128-ctr,aes192-ctr,aes256-ctr
INFO: kex: server: hmac-md5,hmac-sha1,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96
INFO: kex: server: hmac-md5,hmac-sha1,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96
INFO: kex: server: none,zlib
INFO: kex: server: none,zlib
INFO: kex: server:
INFO: kex: server:
INFO: kex: client: diffie-hellman-group1-sha1
INFO: kex: client: ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
INFO: kex: client: aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc
INFO: kex: client: aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc
INFO: kex: client: hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96
INFO: kex: client: hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96
INFO: kex: client: none
INFO: kex: client: none
INFO: kex: client:
INFO: kex: client:
INFO: kex: server->client aes128-ctr hmac-md5 none
INFO: kex: client->server aes128-ctr hmac-md5 none
INFO: SSH_MSG_KEXDH_INIT sent
INFO: expecting SSH_MSG_KEXDH_REPLY
INFO: ssh_rsa_verify: signature true
WARN: Permanently added '192.168.1.39' (RSA) to the list of known hosts.
INFO: SSH_MSG_NEWKEYS sent
INFO: SSH_MSG_NEWKEYS received
INFO: SSH_MSG_SERVICE_REQUEST sent
INFO: SSH_MSG_SERVICE_ACCEPT received
INFO: Authentications that can continue: publickey,keyboard-interactive,password
INFO: Next authentication method: publickey
INFO: Authentications that can continue: keyboard-interactive,password
INFO: Next authentication method: keyboard-interactive
INFO: Authentications that can continue: password
INFO: Next authentication method: password
INFO: Authentication succeeded (password).
Thread Id: 1 Time: 2015-06-18 13:44:07.318 Class: com.mastersam.connectors.unix.UnixConnector Method: executeSetPassword(UnixConnector.java:421) Level: INFO Message: 3004-709 Error changing password for "fikrie".
我也试过改变ssh频道而不是exec。这是更改后的代码:
final ChannelShell channel=(ChannelShell) session.openChannel("shell");
channel.setPty(true);
final Expect4j expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
channel.connect();
final StringBuilder buffer = new StringBuilder();
Closure closure = new Closure() {
public void run(ExpectState expectState) throws Exception {
buffer.append(expectState.getBuffer());
}
};
password.access(new Accessor() {
@Override
public void access(char[] clearChars) {
try {
expect.send(echo -e " + "\"" + new String(clearChars) + "\\n" + new String(clearChars) + "\"" + " | passwd " + userName);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}});