You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Apache MINA SSHD的浏览器终端命令过滤方案咨询

基于Apache MINA SSHD的浏览器终端命令过滤方案

针对你用Apache MINA SSHD(后端)+ JavaScript(前端)实现浏览器终端,需要过滤可用命令的需求,直接给你落地可行的方案:

先明确两个思路的可行性

  1. 缓存变量控制输入:完全可行,也是最直接的落地方向,下文会给出具体实现逻辑。
  2. PTY flags定义可用命令:不可行。PTY flags仅用于配置终端的基础属性(比如终端类型、尺寸、波特率等),和命令权限控制无关联,不用在这个方向浪费精力。

方案一:后端Websocket层拦截校验(快速落地)

因为前端输入是通过Websocket传到后端,再转发给SSH输入流,所以可以在Websocket消息处理环节加拦截逻辑:

核心逻辑

  • 为每个用户会话维护一个命令缓存(比如用StringBuilder),用来拼接用户逐字符输入的内容。
  • 当收到换行符(\n/\r\n)时,判定为命令输入完成,提取命令名和允许列表对比。
  • 允许的命令转发到SSH输入流;不允许的直接向前端返回错误提示,不传给SSH。

Java代码示例

// 每个会话的命令缓存,用会话ID做键
private Map<String, StringBuilder> sessionCmdCache = new ConcurrentHashMap<>();
// 预定义允许的命令列表
private Set<String> allowedCmds = Set.of("ls", "pwd", "cat", "whoami");

// Websocket消息处理方法
public void handleWebsocketMsg(String sessionId, String input) {
    StringBuilder cache = sessionCmdCache.computeIfAbsent(sessionId, k -> new StringBuilder());
    
    // 拆分输入中的换行,处理完整/未完成的命令
    String[] inputParts = input.split("\\r?\\n");
    for (int i = 0; i < inputParts.length; i++) {
        String part = inputParts[i];
        if (i < inputParts.length - 1) {
            // 带换行的部分,是完整命令
            cache.append(part);
            validateAndForwardCmd(sessionId, cache.toString());
            cache.setLength(0); // 清空缓存
        } else {
            // 最后一段无换行,继续缓存
            cache.append(part);
        }
    }
}

// 命令校验与转发
private void validateAndForwardCmd(String sessionId, String cmd) {
    // 提取命令名(简单处理:取第一个空格前的内容)
    String cmdName = cmd.trim().split("\\s+")[0];
    if (allowedCmds.contains(cmdName)) {
        // 允许的命令,写入SSH输入流
        SshClient sshClient = getSshClientBySession(sessionId);
        if (sshClient != null && sshClient.isConnected()) {
            sshClient.getShell().getIn().write((cmd + "\n").getBytes());
            sshClient.getShell().getIn().flush();
        }
    } else {
        // 不允许的命令,返回错误给前端
        sendWebsocketMsg(sessionId, "错误:禁止执行命令 - " + cmdName + "\n");
    }
}

方案二:自定义ShellFactory(SSH原生层处理)

如果想更贴合Apache MINA SSHD的架构,可自定义ShellFactory,在SSH Shell层面处理命令过滤,无需依赖Websocket拦截:

核心逻辑

  • 实现Shell接口,在start方法中接管输入输出流,循环读取用户命令。
  • 校验命令后,要么调用真实Shell执行允许的命令,要么返回错误提示。
  • 配置SshServer使用自定义ShellFactory。

Java代码示例

public class RestrictedShell implements Shell {
    private InputStream in;
    private OutputStream out;
    private OutputStream err;
    private final Set<String> allowedCmds = Set.of("ls", "pwd", "cat");

    @Override
    public void start(Environment env) throws IOException {
        // 启动线程处理命令输入
        new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
                String line;
                // 输出提示符
                out.write("$ ".getBytes());
                out.flush();
                
                while ((line = reader.readLine()) != null) {
                    String cmdName = line.trim().split("\\s+")[0];
                    if (allowedCmds.contains(cmdName)) {
                        // 执行允许的命令,转发到系统Shell
                        Process process = new ProcessBuilder("/bin/sh", "-c", line).start();
                        // 把命令输出转接到SSH输出流
                        IOUtils.copy(process.getInputStream(), out);
                        IOUtils.copy(process.getErrorStream(), err);
                        process.waitFor();
                    } else {
                        err.write(("禁止执行命令:" + cmdName + "\n").getBytes());
                        err.flush();
                    }
                    // 重新输出提示符
                    out.write("$ ".getBytes());
                    out.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    // 实现Shell接口的其他方法
    @Override
    public void setIn(InputStream in) { this.in = in; }
    @Override
    public void setOut(OutputStream out) { this.out = out; }
    @Override
    public void setErr(OutputStream err) { this.err = err; }
    @Override
    public void setSignalListener(SignalListener listener) {}
    @Override
    public void destroy() {}
}

// 配置SshServer使用自定义Shell
SshServer sshd = SshServer.setUpDefaultServer();
sshd.setShellFactory(() -> new RestrictedShell());

命令缓存与校验的补充建议

  1. 会话级缓存:用用户会话ID作为键,将StringBuilder缓存存在线程安全的Map中,避免多用户输入混淆。
  2. 复杂命令处理:如果需要支持管道、重定向等语法,可改用命令解析库(比如Apache Commons Exec)拆解命令结构,精准校验命令名和参数。
  3. 历史命令保存:可在用户会话中维护一个List<String>,记录用户输入过的合法命令,方便后续回溯或统计。

内容的提问来源于stack exchange,提问作者Omar RB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 10:35:31