Skip to content

统一网关为应用提供飞牛 fnOS 访问域名下的稳定系统 URL。发送到网关路径的请求会先由飞牛 fnOS 校验,再转发到应用本地 Unix Socket。

当应用需要复用系统访问域名,同时运行长期服务、支持 WebSocket 或提供 API 时,可以使用统一网关。独立服务访问仍然可以使用端口服务,小型静态页面或简单包兼容场景也可以使用 index.cgi

选择访问模型

能力index.cgi统一网关
简单静态页面适合支持
常驻服务不推荐适合
WebSocket不支持支持
NAS 登录态调用 CGI 前校验转发到服务前校验,并提供用户 Header
性能每次请求启动 CGI 进程转发到长期运行的服务
代码适配成本静态页面或简单包通常改造较少服务需要适配网关路由和鉴权
运行方式每次请求启动 CGI 进程转发到应用服务
常见路径/cgi/ThirdParty/{appname}/index.cgi//app/{appname}

当应用需要暴露自己的独立服务端口,且不需要接入 NAS 登录态时,可以使用端口服务。

工作方式

  1. 应用通过 gatewayPrefix 注册公开路径。
  2. 应用服务监听 gatewaySocket 声明的 Unix Socket。
  3. 用户打开网关路径,例如 /app/myapp
  4. 飞牛 fnOS 校验用户会话。
  5. 飞牛 fnOS 将请求转发到 /var/apps/myapp/target/app.sock
  6. 应用在需要用户上下文时读取网关转发的用户 Header。

入口配置

app/ui/config 中声明网关入口:

json
{
  ".url": {
    "myapp.main": {
      "title": "My App",
      "icon": "images/icon_{0}.png",
      "type": "iframe",
      "protocol": "",
      "gatewayPrefix": "/app/myapp",
      "gatewaySocket": "app.sock",
      "url": "/app/myapp",
      "allUsers": true
    }
  }
}

该配置会注册:

text
/app/myapp

请求会转发到:

text
/var/apps/myapp/target/app.sock

统一网关入口由 gatewayPrefixgatewaySocket 决定。protocolport 会被忽略,不参与统一网关路由。

字段规则

  • protocol:统一网关入口会忽略该字段。

  • port:统一网关入口会忽略该字段。

  • gatewayPrefix

    • 使用 /app/{appname}/app/{appname}/{customPath}
    • 路径应在版本之间保持稳定。
    • 使用简单、URL 安全的名称。公开路径中避免使用点号。
  • gatewaySocket

    • 只填写 Socket 文件名,例如 app.sock
    • Socket 文件应放在已安装应用的 target 目录下。
    • 脚本中可使用 ${TRIM_APPDEST} 定位该目录。

应用要求

  • 应用服务应监听 gatewaySocket 声明的 Unix Socket。
  • Docker 应用也可以使用统一网关。将 ${TRIM_APPDEST} 挂载到容器内,并让容器中的服务在该目录下创建对应的 Socket。
  • HTTP 和 WebSocket 路由应保持在声明的 gatewayPrefix 下。
  • 不要信任客户端传入的用户 ID。请使用飞牛 fnOS 转发的网关鉴权 Header。
  • 读取文件或执行用户相关操作前,请验证请求路径和输入。

会话校验和用户 Header

应用通过统一网关访问时,飞牛 fnOS 会先校验用户会话,再将请求转发给应用。

网关确认用户已登录。应用仍然需要负责自己的业务鉴权规则。

请求通过认证后,网关会通过 Header 转发用户信息:

Header说明示例
X-Trim-Userid当前用户 UID1000
X-Trim-Isadmin当前用户是否为管理员truefalse
X-Trim-Username当前用户名admin

转发到应用的请求示例:

http
GET /app/myapp/list HTTP/1.1
X-Trim-Userid: 1000
X-Trim-Isadmin: true
X-Trim-Username: admin

应用侧使用:

js
function getGatewayUser(req) {
  return {
    uid: req.headers["x-trim-userid"],
    isAdmin: req.headers["x-trim-isadmin"] === "true",
    username: req.headers["x-trim-username"]
  };
}

这些值可以作为网关提供的可信身份上下文使用,但应用仍需要执行自己的业务鉴权。

WebSocket

WebSocket 可以复用同一个网关前缀和 Socket。建议将 WebSocket 路由放在稳定的子路径下,例如:

text
/app/myapp/ws

前端示例:

js
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${wsProtocol}//${window.location.host}/app/myapp/ws`;

const socket = new WebSocket(wsUrl);

socket.onopen = () => {
  socket.send(JSON.stringify({ type: "ping" }));
};

socket.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log(message);
};

通过网关建立的 WebSocket 连接,也会在连接建立时获得同样的身份上下文。连接建立后,应将连接绑定到 X-Trim-Userid

不要信任 WebSocket 消息中由客户端发送的用户 ID。

鉴权和安全

网关校验登录状态,不负责业务权限。应用仍应执行以下规则:

  • 用户只能访问自己的数据。
  • 管理接口需要管理员身份。
  • 高风险操作需要明确的权限检查。
  • 文件路径和记录 ID 需要结合当前用户进行校验。

如果应用通过网关提供文件访问,请限制文件访问范围:

  • 标准化请求路径。
  • 拒绝 .. 目录穿越。
  • 只从预期目录提供文件。
  • 不暴露密钥、数据库、配置文件或私有日志。

OAuth 回调等公开回调路径应保持窄而明确。未鉴权路径只开放所需的 HTTP 方法和数据。


基于非商业使用许可证发布