# webssh **Repository Path**: youngyajun/webssh ## Basic Information - **Project Name**: webssh - **Description**: 基于Spring Boot的Web SSH终端解决方案 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-16 - **Last Updated**: 2026-07-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README

WebSSH

**基于Spring Boot的Web SSH终端解决方案** - **双模部署**:支持作为`Spring Boot Starter`依赖嵌入现有项目,亦可独立部署运行。 - **极简接入**:引入依赖并简单配置,即可快速启用。 - **零客户端**:通过浏览器直接操作`Linux`服务器,无需安装任何额外软件。 ![login](data/md/login.png) # 1. 功能特性 - **Web 终端**:基于 `xterm.js` + `WebSocket`,完整终端体验(`vim`、`top`等全屏程序正常显示) - **多标签页**:同时连接多台主机,标签页拖拽排序,独立会话互不影响 - **分屏终端**:拖拽会话标签到终端区触发分屏,支持水平/垂直方向与嵌套(最多 4 面板),分隔条可拖拽调整比例;切换标签或新建会话不影响 - **文件管理**:`SFTP` 浏览目录、上传/下载文件与目录(目录自动`tar`打包)、文本预览、查看属性 - **主题切换**:`Dark`/`Light`双主题,终端、文件管理器、弹窗等全局联动(顶部导航栏固定暗色) - **安全传输**:登录密码经 RSA 加密传输,私钥一次性使用,用后即焚 - **防暴力破解**:基于`IP`的登录失败计数与自动锁定 - **风险命令拦截**:正则匹配危险命令(如 `rm -rf /`),命中则拒绝执行 - **主机密钥校验**:支持严格校验(防中间人攻击)与免校验两种模式 - **认证方式**:密码认证与私钥认证(可选私钥口令) - **终端自适应**:`PTY`尺寸随浏览器窗口实时同步,`vim`等程序启动即正确填充 - **系统监控**:实时显示远程主机 `CPU`/`RAM`/`Disk`/`Load`/`Net` 指标,阈值告警(≥75% 警告、≥90% 危险),点击指标可查看详情 ![webssh](data/md/webssh.gif) # 2. 技术栈 | 层面 | 技术 | | ---------- | ------------------------------ | | 后端 | Spring Boot 2.3.x ~ 4.0.x(多版本适配)| | JDK | Java 8+(2.x starter)/ Java 17+(3.x/4.x starter)| | SSH 客户端 | JSch(com.github.mwiede:jsch) | | 通信 | WebSocket | | 前端终端 | xterm.js 5.5.0 | | 构建 | Maven 多模块 | ### 多版本适配方案 采用 **共享 core + 多 starter** 架构,一套代码支持 Spring Boot 2.3 ~ 4.0 全系列: ``` Spring Boot 版本 │ 推荐使用 │ 编译基线 ────────────────────┼──────────────────────────────────┼────────────────────────── 2.3 ~ 2.7 │ yyj-webssh-spring-boot2-starter │ SB 2.3.12 + Java 8 3.0 ~ 3.5 │ yyj-webssh-spring-boot3-starter │ SB 3.0.12 + Java 17 4.0 │ yyj-webssh-spring-boot3-starter │ SB 3.0.12 + Java 17(前向兼容) ────────────────────┴──────────────────────────────────┴────────────────────────── 共享核心 │ yyj-webssh-core │ SB 2.3.12 + Java 8(servlet-free) ``` - **2.x starter**:使用 `javax.servlet` + `spring.factories`,Java 8 字节码 - **3.x/4.x starter**:使用 `jakarta.servlet` + `AutoConfiguration.imports`,Java 17 字节码 - **core 模块**:servlet-free,所有版本通用的 SSH/PTY/RSA 业务逻辑 > **版本号策略**:core 的版本号独立于 starter 演进。core 1.0.0 是共享核心的首发版本,被 starter-2x 2.0.0 与 starter-3x 3.0.0 同时依赖,并非"旧版"。starter 版本号首位对应所适配的 Spring Boot 大版本(2.x / 3.x)。 # 3. 项目结构 ``` webssh/ # 父 POM (java=21, sb=4.0.6) ├── pom.xml # 父 POM(依赖管理) ├── yyj-webssh-core/ # 共享核心(servlet-free, java=8, sb=2.3.12) │ └── src/main/java/com/webssh/ │ ├── config/WebSshProperties.java # 配置属性类 │ ├── ssh/ # SSH/PTY/本地文件服务(核心业务) │ ├── util/RsaUtil.java # RSA 工具(servlet-free 版本) │ └── security/LoginAttemptService.java # 登录限制服务(IP 由调用方传入) │ ├── yyj-webssh-spring-boot3-starter/ # spring-boot3-starter(SB 3.0~4.0, java=17, sb=3.0.12) │ └── src/main/ │ ├── java/com/webssh/ │ │ ├── config/WebSshAutoConfiguration.java # 自动配置类 │ │ ├── controller/ # 认证、文件管理、页面控制器 │ │ ├── security/ # 认证拦截器、ClientIpExtractor │ │ ├── util/RsaSessionHelper.java # RSA 助手(jakarta.servlet 版本) │ │ └── websocket/ # WebSocket 配置与处理器 │ └── resources/META-INF/ │ ├── resources/webssh/ # 前端页面(HTML/CSS/JS/xterm) │ └── spring/ # AutoConfiguration.imports │ ├── yyj-webssh-spring-boot2-starter/ # spring-boot2-starter(SB 2.3~2.7, java=8, sb=2.3.12) │ └── src/main/ │ ├── java/com/webssh/ # 与默认 starter 同名同结构(import javax.servlet) │ └── resources/META-INF/ │ ├── resources/webssh/ # 前端页面(同上) │ └── spring.factories # SB 2.x 自动装配入口 │ └── webssh-app/ # 示例应用(SB 4.0.6 + Java 21,可直接运行) └── src/main/ ├── java/com/yyj/ # 启动类(启动后自动打开浏览器) └── resources/ ├── application.yml # 端口、上传限制等 └── application-webssh.yml # WebSSH 配置 ``` # 4. 快速开始 ## 4.1 构建项目 ```bash # 在项目根目录执行(需 Java 21 + Maven) mvn clean install -DskipTests ``` 构建产物: - `yyj-webssh-core/target/yyj-webssh-core-1.0.0.jar` — 共享核心制品 - `yyj-webssh-spring-boot3-starter/target/yyj-webssh-spring-boot3-starter-3.0.0.jar` — spring-boot3-starter 制品(SB 3.x/4.x) - `yyj-webssh-spring-boot2-starter/target/yyj-webssh-spring-boot2-starter-2.0.0.jar` — spring-boot2-starter 制品(SB 2.x) - `webssh-app/target/webssh-app.jar` — 可直接运行的示例应用(SB 4.0.6) ## 4.2 运行示例应用 ```bash java -jar webssh-app/target/webssh-app.jar ``` 启动后自动打开浏览器访问登录页,也可手动访问 `http://localhost:8080/webssh/login.html`。 ## 4.3 接入已有 Spring Boot 项目 **步骤一:根据 Spring Boot 版本选择对应的 starter** | 你的 Spring Boot 版本 | 依赖 artifactId | JDK 要求 | |------------------------|------------------------------|----------| | 2.3.x ~ 2.7.x | `yyj-webssh-spring-boot2-starter` | Java 8+ | | 3.0.x ~ 3.5.x | `yyj-webssh-spring-boot3-starter` | Java 17+ | | 4.0.x | `yyj-webssh-spring-boot3-starter` | Java 17+ | > **判断依据**:SB 2.x 使用 `javax.servlet`,SB 3.x/4.x 使用 `jakarta.servlet`,命名空间不同需选用对应 starter。 **步骤二:添加 Maven 依赖** > **必读**:`yyj-webssh-core` 是 WebSSH 的共享核心模块(包含 SSH/PTY/本地文件/RSA/登录限制等业务逻辑),与 starter 是**两个独立版本号**的制品(core 1.0.0、starter-2x 2.0.0、starter-3x 3.0.0)。引入 starter 时**必须同时引入 core**,二者缺一不可。 以 Spring Boot 3.x/4.x 项目为例: ```xml io.github.youngyajun yyj-webssh-core 1.0.1 io.github.youngyajun yyj-webssh-spring-boot3-starter 3.0.0 io.github.youngyajun yyj-webssh-core io.github.youngyajun yyj-webssh-spring-boot3-starter ``` 若是 Spring Boot 2.x 项目,将`starter`的`artifactId`替换为 `yyj-webssh-spring-boot2-starter`,版本号改为 `2.0.0`,`core`保持不变: ```xml io.github.youngyajun yyj-webssh-core 1.0.1 io.github.youngyajun yyj-webssh-spring-boot2-starter 2.0.0 io.github.youngyajun yyj-webssh-core io.github.youngyajun yyj-webssh-spring-boot2-starter ``` > **依赖说明**: > - `yyj-webssh-core` 与 `yyj-webssh-spring-boot*-starter` 必须同时引入,二者缺一不可 **步骤三:配置 `application-webssh.yml`** ```yaml # WebSSH 配置 # ============================================================================ # 文件上传/下载大小限制说明 # ---------------------------------------------------------------------------- # 1. 上传(POST /webssh/api/upload) # - 受 Spring Boot 全局 multipart 配置约束,配置项位于 application.yml: # spring.servlet.multipart.max-file-size 单文件最大大小(默认 1MB) # spring.servlet.multipart.max-request-size 整个请求最大大小(默认 10MB) # - 超过限制会抛 MaxUploadSizeExceededException,前端收到 500 错误 # - 若使用反向代理(如 Nginx),还需同步调整 client_max_body_size # 以及 Tomcat 的 server.tomcat.max-swallow-size / max-http-form-post-size # # 2. 下载(GET /webssh/api/download) # - 代码层面无大小限制,采用 SFTP/tar 流式传输(4KB 缓冲区) # - 实际上限受 HTTP 超时、代理超时(proxy_read_timeout)等网络层约束 # # 3. 文件预览(GET /webssh/api/preview) # - 硬编码 2MB 限制(WebSshFileController 中 maxSize = 2 * 1024 * 1024) # - 超过会提示“文件过大,不支持预览,请直接下载”,如需调整需改源码 # ============================================================================ webssh: # 是否启用(设为 false 则完全关闭 WebSSH) enabled: true # WebSSH 界面访问路径前缀 context-path: /webssh # SSH 连接超时(毫秒) timeout: 10000 # 终端类型 terminal-type: xterm-256color # 字符编码 charset: UTF-8 # 主机密钥校验:yes=严格校验(推荐生产环境),no=不校验(默认) host-key-verification: no # known_hosts 路径(host-key-verification=yes 时使用) # known-hosts: ~/.ssh/known_hosts # ============================================================================ # WebSSH 管理界面登录账号配置(单账号与多账号可共存,用户名冲突时以多账号为准) # ---------------------------------------------------------------------------- # 【密码强度规则】长度 8~19 位(>=8 且 <20),必须同时包含: # - 大写字母 A-Z # - 小写字母 a-z # - 数字 0-9 # 不符合规则的账号视为无效,启动时仅告警并跳过(不影响启动),但无法用于登录。 # 例如 "Abc12345" 合规;"webssh"(无大写无数字、长度不足)不合规,会被跳过。 # # 方式一:单账号模式(向后兼容) # # 方式二:多账号模式(推荐) # accounts: # - username: admin # password: Admin@2024 # - username: ops # password: Ops#2024abc # # 共存规则: # - 两者同时配置时,会合并为最终账号列表共同生效 # - 若单账号的 username 与 accounts 中某账号同名,则忽略单账号(以多账号为准) # - 两者均未配置时不阻止启动,仅告警(登录将全部失败) # ============================================================================ # WebSSH 管理界面登录账号(单账号模式,无默认值,启动时校验) username: webssh # WebSSH 管理界面登录密码(单账号模式,无默认值,须符合上方强度规则) # 注意:默认值 "webssh" 不符合强度规则,会被判为无效账号无法登录,请务必修改 password: Webssh-2026 # 多账号列表(与 username/password 共存;用户名冲突时以多账号为准) accounts: [ # {username: test, password: 123456}, # {username: root, password: 123456} ] # SSH 主机列表 hosts: # username&password 模式 - name: 【192.168.1.166】 host: 192.168.1.166 port: 22 # 可以不配置username&password,登录后在界面手动输入 username: password: # privateKey&passphrase 模式 # - name: 【xxx】 # host: xxx.xxx.xxx.xxx # port: 22 # privateKey: /path/to/id_rsa # passphrase: private-key-password # 本地PTY模式示例:webssh与目标机同机部署时,直接启动本地 shell,无需SSH凭据 # 仅支持 Linux/Mac等类Unix系统(Windows 暂不支持,将报错) # - name: 【本机-PTY直连】 # host: localhost # type: local # 高风险命令正则列表(命中任意一条则拒绝通过终端执行) high-risk-commands: # === 文件系统破坏 === - '^rm\s+-rf\s+/' # rm -rf / (删除根目录) - '^rm\s+-rf\s+/\*' # rm -rf /* (删除根目录下所有文件) - '^rm\s+-rf\s+~' # rm -rf ~ (删除家目录) - '>\s*/dev/sd[a-z]' # > /dev/sda (覆盖磁盘) - 'dd\s+if=.*of=/dev/' # dd 直接写磁盘 - 'mkfs\.' # mkfs.* (格式化文件系统) # === 权限失控 === # - '^chmod\s+-R\s+777\s+/' # chmod -R 777 / (开放所有权限) # - '^chown\s+-R\s+\w+:\w+\s+/' # chown -R 递归改所有者 # === 系统关机/重启 === - '^shutdown' # shutdown # - '^reboot' # reboot # - '^halt' # halt # - '^poweroff' # poweroff # - '^init\s+[06]' # init 0 / init 6 # === 进程与磁盘 === - 'fdisk\s+/dev/' # fdisk 磁盘分区 - 'parted\s+/dev/' # parted 磁盘分区 # 登录安全策略(防暴力破解) login-security: max-fail-attempts: 5 # 最大失败次数(0 或负数=关闭限制) lock-minutes: 5 # 锁定时长(分钟) trust-forwarded-for: false # 是否信任 X-Forwarded-For(反代场景设为 true) ``` **步骤四:引入application-webssh.yml** 主配置`application.yml`文件引入配置: ```yml # Spring 相关配置 spring: profiles: # 激活的配置文件(对应 application-webssh.yml) active: webssh ``` **步骤五:启动应用** 正常启动`Spring Boot`应用即可,Starter 会自动装配(默认 starter 通过 `AutoConfiguration.imports`,spring-boot2-starter 通过 `spring.factories`),无需额外注解或配置类。 访问 `http://your-host:port/webssh/login.html`,使用 `webssh.username` / `webssh.password` 登录后选择主机连接。 # 5. 配置项参考 | 配置项 | 默认值 | 说明 | |--------|--------|------| | `webssh.enabled` | `true` | 是否启用 WebSSH | | `webssh.context-path` | `/webssh` | 界面访问路径前缀 | | `webssh.username` | 无(必填) | 管理界面登录账号 | | `webssh.password` | 无(必填) | 管理界面登录密码 | | `webssh.timeout` | `5000` | SSH 连接超时(毫秒) | | `webssh.terminal-type` | `xterm` | 终端类型 | | `webssh.charset` | `UTF-8` | 字符编码 | | `webssh.host-key-verification` | `no` | 主机密钥校验(`yes`/`no`) | | `webssh.known-hosts` | `~/.ssh/known_hosts` | known_hosts 文件路径 | | `webssh.hosts` | 空 | SSH 主机列表 | | `webssh.high-risk-commands` | 空 | 高风险命令正则列表 | | `webssh.login-security.max-fail-attempts` | `5` | 最大登录失败次数 | | `webssh.login-security.lock-minutes` | `5` | IP 锁定时长(分钟) | | `webssh.login-security.trust-forwarded-for` | `false` | 是否信任代理转发头 | ## 5.1 主机配置项(`webssh.hosts[*]`) | 属性 | 说明 | |------|------| | `name` | 主机显示名称 | | `host` | 主机 IP 或域名 | | `port` | SSH 端口,默认 `22` | | `username` | SSH 用户名(可选,不填则界面手动输入) | | `password` | SSH 密码(可选,与 `privateKey` 二选一) | | `privateKey` | 私钥文件路径(可选) | | `passphrase` | 私钥口令(可选) | > **凭据来源优先级**:界面手动输入 > 配置文件预填。预填凭据的连接更便捷,但不填则更安全(登录后手动输入 SSH 凭据)。 ## 5.2 文件上传大小限制 上传接口受 Spring Boot 全局 multipart 配置约束: ```yaml spring: servlet: multipart: max-file-size: 100MB # 单文件上限 max-request-size: 500MB # 单次请求上限 ``` 若使用 Nginx 反向代理,还需调整 `client_max_body_size`。下载接口为流式传输,无大小限制。 ## 5.3 安全建议 1. **修改默认凭据**:`webssh.username` 和 `webssh.password` 无默认值,启动时强制校验,请使用强密码 2. **生产环境开启主机密钥校验**:设置 `host-key-verification: yes` 并配置 `known-hosts` 3. **配置高风险命令拦截**:根据团队规范添加正则规则,防止误操作 4. **反代场景注意 IP 识别**:使用 Nginx 时设置 `trust-forwarded-for: true` 以识别真实客户端 IP 5. **使用 HTTPS**:通过反向代理启用 TLS,保护 WebSocket 和登录凭据传输 ## 5.4 集成 Spring Security(可选) 如果接入项目已启用 `Spring Security`,`WebSSH` 的页面、认证接口、文件管理 API、`WebSocket` 通道都会被 Security 拦截,需要显式放行。最简单的做法是将整个 `webssh.context-path`(默认 `/webssh`)下的所有路径全部 `permitAll()`。 > **说明**:WebSSH Starter 内部已自带登录鉴权(基于 `webssh.username` / `webssh.password` + RSA 加密 + IP 锁定 + 登录失败计数)。放行后 WebSSH 仍会走自身登录流程,不会"裸奔"。无需在 Spring Security 侧重复鉴权。 ### 5.4.1 Spring Boot 2.x(Spring Security 5.x,`javax`) ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/webssh/**").permitAll() .anyRequest().authenticated() .and() // 关闭 CSRF,否则登录 POST /webssh/auth/login 会被拦截返回 403 .csrf().disable(); } } ``` ### 5.4.2 Spring Boot 3.x / 4.x(Spring Security 6.x,`jakarta`) Spring Security 6 已移除 `WebSecurityConfigurerAdapter`,改用 `SecurityFilterChain` Bean: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/webssh/**").permitAll() .anyRequest().authenticated() ) // 关闭 CSRF,否则登录 POST /webssh/auth/login 会被拦截返回 403 .csrf(csrf -> csrf.disable()); return http.build(); } } ``` ### 5.4.3 注意事项 1. **CSRF 必须关闭或针对 `/webssh/**` 排除**:WebSSH 登录走 `POST /webssh/auth/login`,开启 CSRF 后会被拦截返回 403 2. **`context-path` 自定义**:若通过 `webssh.context-path` 修改了前缀(如 `/myapp/webssh`),需将上例中的 `/webssh/**` 同步替换 3. **子路径部署**:与 [6.5 子路径部署场景](#65-子路径部署场景) 配合使用时,路径前缀需对齐 4. **WebSocket 与 Spring Security**:`/webssh/ws` 走 HTTP 升级握手,按上方规则放行即可,无需额外处理;若项目对 WS 有特殊鉴权过滤器,需单独排除 5. **会话冲突**:WebSSH 内部使用 `HttpSession` 存储登录态与 RSA 私钥(一次性使用)。若接入项目也使用 Spring Session / Session 共享,请确保 `webssh.*` 相关 session attribute 不会被外部逻辑误清理 ## 5.5 API 接口 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/webssh/auth/public-key` | 获取 RSA 公钥(用于密码加密) | | POST | `/webssh/auth/login` | 登录 | | POST | `/webssh/auth/logout` | 登出 | | GET | `/webssh/auth/check` | 检查登录状态 | | GET | `/webssh/api/hosts` | 获取主机列表 | | GET | `/webssh/api/hosts/info` | 获取主机详情 | | POST | `/webssh/api/connect` | 建立文件管理 SSH 会话 | | POST | `/webssh/api/disconnect` | 断开文件管理会话 | | GET | `/webssh/api/files` | 列出目录内容 | | GET | `/webssh/api/pwd` | 获取当前工作目录 | | GET | `/webssh/api/suggest` | 路径自动补全 | | GET | `/webssh/api/preview` | 预览文本文件(≤2MB) | | GET | `/webssh/api/download` | 下载文件/目录 | | POST | `/webssh/api/upload` | 上传文件 | | GET | `/webssh/api/stat` | 获取文件/目录属性 | | GET | `/webssh/api/calcSize` | 计算目录大小 | | POST | `/webssh/api/resolve-cwd` | 解析`cd`命令后的工作目录 | | POST | `/webssh/api/exec` | 执行非交互式命令 | | GET | `/webssh/api/monitor` | 获取系统监控概览(CPU/RAM/Disk/Load/Net) | | GET | `/webssh/api/monitor/detail` | 获取指定指标详情(`type=cpu`/`mem`/`disk`/`load`/`net`) | | WS | `/webssh/ws` | WebSocket 终端通道 | > 路径前缀 `/webssh` 可通过 `webssh.context-path` 自定义。 # 6. Nginx 反向代理配置 当 WebSSH Starter 被嵌入到现有项目并通过 Nginx 暴露对外服务时,需要正确配置反向代理,特别是 WebSocket 升级和长连接超时。 ## 6.1 路由结构 默认 `webssh.context-path=/webssh`,所有路径都在该前缀下: | 类型 | 路径 | 说明 | |------|------|------| | 静态资源 | `/webssh/login.html`、`/webssh/index.html`、`/webssh/images/**`、`/webssh/js/**`、`/webssh/style/**` | 前端页面与静态资源 | | 页面跳转 | `/webssh`、`/webssh/logout` | 重定向到 `index.html` | | 认证 API | `/webssh/auth/**` | 登录、登出、公钥、状态检查 | | 文件 API | `/webssh/api/**` | 文件管理、监控、命令执行 | | **WebSocket** | `/webssh/ws` | 终端通道(query 参数:`host`、`cols`、`rows`、`username`、`password`、`keyId`) | ## 6.2 关键约束(重要) 前端 JS 从浏览器地址栏动态推断 `contextPath`: ```javascript const contextPath = window.location.pathname.replace(/\/[^/]*$/, ''); let wsUrl = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}${contextPath}/ws?...`; fetch(contextPath + '/api/...'); ``` 由此衍生两条硬性约束: 1. **不能 rewrite 路径**:`proxy_pass` 末尾不能加 `/`(不要写成 `proxy_pass http://backend/;`),否则 URI 被改写后前端推断的 contextPath 与后端实际路径错位 2. **WebSocket 协议自动跟随**:站点是 HTTPS 时前端自动用 `wss://`,Nginx 这边 SSL 终止后用 HTTP 转给后端即可,后端无需配置 SSL ## 6.3 推荐配置(HTTPS + WebSocket) 假设后端应用监听 `127.0.0.1:8080`,对外域名 `example.com`: ```nginx # HTTP -> HTTPS 跳转 server { listen 80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name example.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; # ZMODEM rz/sz 大文件 + 文件管理器上传,按需调整 client_max_body_size 1024m; # ============ WebSSH WebSocket(必须单独 location 配置升级头)============ location ^~ /webssh/ws { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket 升级头(仅此 location 需要) proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # ZMODEM rz/sz 大文件传输 + 长会话 SSH 操作,必须放宽超时 proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_buffering off; } # ============ WebSSH 其他路径(页面、静态资源、auth、api)============ location ^~ /webssh/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # ============ 业务应用其他路径 ============ location / { proxy_pass http://127.0.0.1:8080; # 或 try_files $uri $uri/ /index.html; (前端 SPA 场景) } } ``` ## 6.4 关键点说明 ### 6.4.1 为什么必须用 `^~` Nginx location 匹配优先级:`=` > `^~` > 正则 > 普通前缀。若 WebSSH 用普通前缀 `location /webssh/`,会被如下正则规则截走: ```nginx location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { ... } # 拦截 /webssh/images/*.png location ~ .*\.(js|css)?$ { ... } # 拦截 /webssh/js/*.js、/webssh/style/*.css ``` 这些规则通常只设置 `expires` 缓存头,没有 `proxy_pass`,会用 `try_files` 兜底成 `index.html`,导致前端 JS/CSS/图片加载失败。加 `^~` 后,`/webssh/` 下所有请求都走反代规则,不再被正则匹配。 ### 6.4.2 为什么 WebSocket 必须独立 location `Upgrade`、`Connection` 升级头只对 `/webssh/ws` 路径生效。若加到普通 HTTP location 上,会污染静态资源请求。Nginx 按 `^~` 最长前缀匹配,`/webssh/ws` 会命中独立 location,普通请求走 `/webssh/`。 ### 6.4.3 超时与缓冲 | 配置项 | 推荐值 | 原因 | |--------|--------|------| | `proxy_read_timeout` | `3600s` | ZMODEM 大文件传输、长 SSH 会话默认 60s 会被切断 | | `proxy_send_timeout` | `3600s` | 同上 | | `proxy_buffering` | `off`(仅 ws) | 关闭缓冲确保终端实时响应 | | `client_max_body_size` | `1024m` | 文件管理器上传大文件需要 | ### 6.4.4 IP 锁定功能(可选) 默认 `webssh.login-security.trust-forwarded-for=false`,不信任 `X-Forwarded-For` 等转发头(防伪造)。若希望登录失败锁定基于真实客户端 IP,需要在 `application.yml` 中开启: ```yaml webssh: login-security: trust-forwarded-for: true ``` 同时 Nginx 必须传递 `X-Forwarded-For` / `X-Real-IP`(上方配置已包含)。 ## 6.5 子路径部署场景 若整个应用部署在子路径下(如 `/myapp/`),需同步调整 WebSSH 的 `context-path`: ```yaml webssh: context-path: /myapp/webssh ``` Nginx 配置: ```nginx location ^~ /myapp/webssh/ws { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_buffering off; } location ^~ /myapp/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } ``` ## 6.6 验证 部署后访问 `https://example.com/webssh/login.html`: - 登录页能打开 → 静态资源 `location` 正常 - 登录成功 → `auth API` 反代正常 - 打开终端能连 SSH → WebSocket 升级头配置正确 - `rz`/`sz` 传大文件不断 → `proxy_read_timeout 3600s` 生效 # 7. WebSSH相关项目推荐 **Guacamole**:[https://github.com/apache/guacamole-server](https://github.com/apache/guacamole-server) **ttyd**:[https://github.com/tsl0922/ttyd](https://github.com/tsl0922/ttyd) # 8. 致谢 本项目站在以下优秀开源项目的肩膀上,衷心感谢它们的作者与社区: - [JSch](https://github.com/mwiede/jsch) — SSH 协议客户端 - [pty4j](https://github.com/JetBrains/pty4j) — 本地 PTY 终端支持(JetBrains 开源) - [xterm.js](https://github.com/xtermjs/xterm.js) — 浏览器端终端模拟器 - [zmodem.js](https://github.com/FGasper/zmodemjs) — ZMODEM 协议实现(rz/sz 文件传输) - [JSEncrypt](https://github.com/travist/jsencrypt) — RSA 加密库(登录密码传输加密) 感谢所有为开源社区贡献力量的人。