Jsch实践(二):JSch的HostKeyRepository接口如何使用
JSch 的 HostKeyRepository 接口用于存储和验证远程服务器的 SSH 主机密钥。当连接到一个新的远程服务器时,JSch 会请求服务器的主机密钥,并期望用户确认是否接受这个密钥。在生产环境中,你不应该每次都接受一个新的主机密钥,因为这可能是一个中间人攻击的标志。相反,你应该使用一个 HostKeyRepository 来存储之前接受过的密钥,并自动验证后续的连接。
下面是一个简单的示例,展示了如何使用 JSch 的 HostKeyRepository 接口来存储和验证主机密钥:
import com.jcraft.jsch.*; import java.util.Properties; public class JschHostKeyRepositoryExample { public static void main(String[] args) { String host = "your.remote.host"; int port = 22; String user = "your_username"; String password = "your_password"; JSch jsch = new JSch(); // 使用一个自定义的 HostKeyRepository jsch.setHostKeyRepository(new MyHostKeyRepository()); Session session = null; try { session = jsch.getSession(user, host, port); session.setPassword(password); // 设置严格的主机密钥检查 Properties config = new Properties(); config.put("StrictHostKeyChecking", "yes"); session.setConfig(config); // 连接到服务器 session.connect(); // 打开一个执行命令的通道 ChannelExec channelExec = (ChannelExec) session.openChannel("exec"); InputStream in = channelExec.getInputStream(); // 设置命令,这里假设脚本有执行权限 channelExec.setCommand("sh " + scriptPath); // 连接到远程命令的输入/输出/错误流 channelExec.connect(); // 读取命令输出 byte[] tmp = new byte[1024]; while (true) { while (in.available() > 0) { int i = in.read(tmp, 0, 1024); if (i < 0) break; System.out.print(new String(tmp, 0, i)); } if (channelExec.isClosed()) { if (in.available() > 0) continue; System.out.println("exit-status: " + channelExec.getExitStatus()); break; } try { Thread.sleep(1000); } catch (Exception ee) { // 忽略 } } // 断开连接 channelExec.disconnect(); // 断开连接 session.disconnect(); } catch (JSchException e) { e.printStackTrace(); } finally { if (session != null && session.isConnected()) { session.disconnect(); } } } static class MyHostKeyRepository implements HostKeyRepository { // 使用一个内部存储来保存主机密钥 private final java.util.HashMap<String, HostKey> hostKeys = new java.util.HashMap<>(); @Override public HostKey check(String host, Session session) { // 检查我们是否之前存储了这个主机密钥 return hostKeys.get(host); } @Override public void add(HostKey hostkey, String host) { // 添加新的主机密钥到存储中 hostKeys.put(host, hostkey); } @Override public void remove(String host, String type) { // 根据主机和类型移除主机密钥,这里我们简化处理,不实现这个方法 } @Override public void remove(String host) { // 根据主机移除主机密钥,这里我们简化处理,不实现这个方法 } @Override public String getHostKeyAlias(String host) { // 获取主机密钥的别名,这里我们简化处理,不实现这个方法 return null; } @Override public void setHostKeyAlias(String host, String alias) { // 设置主机密钥的别名,这里我们简化处理,不实现这个方法 } }
}
在上面的代码中,你需要替换your.remote.host、your_username、your_password和/path/to/your/script.sh为实际的远程服务器地址、用户名、密码和脚本路径。
注意:
-
“StrictHostKeyChecking”, “no” 设置为 no 意味着在连接过程中不会检查远程主机密钥的合法性。在生产环境中,这是一个不安全的做法,因为它容易受到中间人攻击。你应该使用JSch的HostKeyRepository接口来安全地存储和验证主机密钥。
-
这个示例没有处理异常或错误,只是简单地将它们打印到控制台。在实际的应用程序中,你应该根据需要对异常和错误进行适当处理。
-
如果你的脚本需要输入参数,你可以将它们附加到setCommand方法的字符串中,例如channelExec.setCommand(“sh " + scriptPath + " arg1 arg2”)。
-
在实际部署时,密码不应硬编码在代码中,而应该使用环境变量、配置文件或安全的密钥管理系统来管理。
-
确保远程服务器上的脚本具有执行权限,否则脚本无法运行。你可以使用chmod +x /path/to/your/script.sh命令来添加执行权限。