ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OpenChamber 浏览器资源加载与 URL 鉴权:runtimeFetch、oc_url_token 与 dev-tunnel 的完整实现解析

OpenChamber 浏览器资源加载与 URL 鉴权:runtimeFetch、oc_url_token 与 dev-tunnel 的完整实现解析 AI Agent人工智能代码智能体交互助手【免费下载链接】openchamberAgentic Development Environment based on OpenCode AI agent项目地址https://gitcode.com/gh_mirrors/op/openchamber点击查看免费下载导读OpenChamber 是一个基于 OpenCode AI Agent 的 Agentic 开发环境其 UIWeb / Electron / Mobile需要在前端安全地加载运行时runtime托管的各类资源Markdown 图片、SSE 事件流、WebSocket、受保护的原生文件甚至远程开发机上的第三方 dev server 页面。本篇文章以 OpenChamber 仓库中.agents/skills/ui-api-decoupling/references/browser-assets-and-auth.md为核心骨架结合packages/ui、packages/web的源码实现系统讲解「该用哪种方式取资源」「浏览器拥有的 URL 如何鉴权」「如何不做任何改写地展示第三方页面」三大问题读完即可在自己的前端集成中正确复用这套模式。一、请求归属者决定路径先选择正确的资源获取方式OpenChamber 的核心原则是先区分谁拥有这个请求Request Owner再决定使用哪条路径。普通 HTTP 调用由 UI 进程直接发起可以携带Authorization头而 iframe、WebSocket、SSE、下载链接这类由浏览器本身占有的 URL无法附加自定义请求头必须走 URL 鉴权。关联文档给出的决策表如下请求形态正确路径UI 可以 fetch 一个小的受保护资源runtimeFetch读取blob()渲染为 object URL浏览器必须拥有一个 URLiframe、下载/打开链接、大图/原始图、被重写的子资源getRuntimeUrlResolver().authenticatedAsset(...)SSEgetRuntimeUrlResolver().sse(...)并配合自有的传输层WebSocketgetRuntimeUrlResolver().websocket(...)必要时配合openRuntimeWebSocket决策表中有一条明确的反模式警告不要先预构建一个浏览器 URL然后立刻拿它去调runtimeFetch。普通 HTTP 调用方把路由路径交给runtimeFetch浏览器/实时消费者使用 resolver 生成的 URL。两者的鉴权语义完全不同——前者走Authorization头后者走 URL 查询参数里的短时令牌混用会把令牌从头错误地泄露到URL中。1.1 runtimeFetch可携带请求头的运行时 HTTP 客户端runtimeFetch的实现位于 packages/ui/src/lib/runtime-fetch.ts它对window.fetch做了两层包装路径解析buildRuntimeFetchUrl/api/*、/auth/*、/health这类运行时路径会被解析为当前运行时的真实地址可能通过注入的__OPENCHAMBER_API_BASE_URL__见 packages/ui/src/lib/runtime-url.ts外部绝对 URL 若非当前窗口同源则原样放行。鉴权头注入mergeHeaders→buildRuntimeAuthHeaders只有命中运行时服务同源且属于/api、/auth、/health白名单的请求才会附加Authorization: Bearer ...和运行时额外头。此外runtimeFetch还包含两个值得注意的实现细节读请求合并read coalescing对/api/(config|path|app/agents|agent|project|command)的并发 GET 会在请求进行中合并去重每个调用者拿到独立的clone()。合并仅限 GET、无AbortSignal、且非事件流避免一个调用方的取消影响其他调用方见 runtime-fetch.ts。Relay 模式路由当活跃运行时是私有 relayE2EE 加密隧道时运行时 HTTP 不再走网络而是乘坐隧道转发同样的路径、同样的鉴权头只是传输层不同见 runtime-fetch.ts。installRuntimeFetchBridge见 runtime-fetch.ts还会把全局window.fetch桥接为runtimeFetch语义保证整个 UI 树里的普通fetch调用行为一致。1.2 RuntimeUrlResolver为浏览器拥有的 URL生成 URLpackages/ui/src/lib/runtime-url.ts 定义了RuntimeUrlResolver接口createRuntimeUrlResolver生成具体实现api(path, query)普通 API URL不带令牌authenticatedAsset(path, query)给 URL 附加oc_url_token查询参数assetWithUrlToken(path, token, query)与authenticatedAsset相同但使用调用方自行铸造的作用域令牌例如 guest 场景见下文sse(path, query)构建 SSE URL必要时走本地实时代理websocket(path, query)把 HTTP(S) URL 转换为ws(s)协议必要时走/api/openchamber/realtime-proxy/ws代理rawFile(path, options)生成/api/fs/raw的受保护文件 URL支持download、allowOutsideWorkspace、outsideFileGrant参数。注意withUrlAuth见 runtime-url.ts是唯一在 URL 上追加oc_url_token的地方——这也是绝不手动拼接oc_url_token这一规则的代码级保障。1.3 一个真实例子Markdown 图片加载packages/ui/src/components/chat/markdown/markdownImageAssets.ts 是先runtimeFetch取小资源、再生成 object URL的典型范例服务端/api/.../markdown-image-grants一次性审批当前消息的图片清单前端用runtimeFetch读取受保护图片经过 MIME 类型、文件大小上限 10 MiB与文件签名校验对于浏览器必须拥有 URL的场景大图预览、下载则调用getRuntimeUrlResolver().authenticatedAsset(/api/fs/raw, { path, ... })见 markdownImageAssets.ts。二、Object URL小资源的安全展示与缓存回收当 UI 可以自行 fetch 资源时应读取为Blob并用URL.createObjectURL生成本地 object URL 渲染。关联文档给出了四条 Object URL 使用准则结合源码可以进一步理解缓存键cache key必须完整键应包含运行时身份runtime identity、实体 ID、更新/版本号、渲染选项。运行时身份很关键——从 runtime-switch.ts 的命名可以推断OpenChamber 支持在多个运行时端点之间切换一旦切换旧的缓存必须作废。大值资源要双重限界既限制条目数量count也限制总字节bytes。markdownImageAssets.ts中的MAX_PREPARE_CACHE_ENTRIES 1024就是按条数限界的例子见 markdownImageAssets.ts。驱逐即回收object URL 被逐出缓存时必须调用URL.revokeObjectURL避免 blob 内存无法被 GC 回收。确定性兜底渲染加载中或仅展示性失败display-only failure时渲染一个确定的 fallback而不是闪烁或空白。这些准则背后的动机是object URL 只存在于当前页面上下文不会进入浏览器历史、不会被缓存复用、不携带任何鉴权信息因此是UI 自己取资源场景下最安全的展示形态。三、URL Token浏览器拥有 URL 时的鉴权方案iframe、img、下载链接、SSE、WebSocket 等由浏览器发起的请求无法附加自定义的Authorization头。OpenChamber 的解决方案是通过运行时鉴权助手铸造短时、作用域受限的oc_url_token附加在 URL 查询参数上。3.1 铸造端runtime-auth.tspackages/ui/src/lib/runtime-auth.ts 实现了完整的 URL token 生命周期铸造向/auth/url-token发送一次 POST携带当前运行时凭据返回{ token, expiresAt }。在 relay 模式下铸造请求同样要乘坐隧道而非走网络见 runtime-auth.ts。作用域铸造mintGuestFrameUrlAuthToken(guestId)铸造scopeguest:id的令牌只对/api/guests/id/下的文件有效。因为 guest iframe 的 URL 对 guest 自己的脚本可见令牌必须在其他任何地方都毫无价值见 runtime-auth.ts。原子替换新令牌铸造成功后通过setRuntimeUrlAuthToken原子换入旧令牌在换入前始终有效不存在空令牌窗口并发调用共享同一个 in-flight 请求见 runtime-auth.ts。主动刷新调度器acquireRuntimeUrlAuthToken注册一个消费者只要有消费者存在就在令牌过期前提前 10 秒的 skew 窗口 5 秒缓冲主动续期最后一个消费者释放后停止轮询避免后台空转打/auth/url-token见 runtime-auth.ts。3.2 三条铁律关联文档明确了三条不可妥协的规则绝不手动拼接oc_url_token只能通过authenticatedAsset/assetWithUrlToken生成。绝不把长寿命的客户端 bearer token 放进 URLURL 会出现在历史记录、日志、Referer 中只能放短时令牌。oc_client_token作为查询参数使用仅被视为 legacy只做剥离/拒绝处理即服务端遇到 URL 中的oc_client_token应当拒绝或剥离而不是接受。3.3 校验端ui-auth.js 的白名单与作用域服务端校验在 packages/web/server/lib/ui-auth/ui-auth.js 中完成令牌形如oc_url_base64url服务端内存 Map 中登记了它对应的会话令牌、过期时间与作用域见 ui-auth.js。TTL 固定为 60 秒URL_AUTH_TOKEN_TTL_MS 60 * 1000见 ui-auth.js过期即删除。HTTP 可读路径白名单isUrlAuthReadableHttpPath见 ui-auth.js只允许以下 GET 路径携带 URL token/api/event、/api/global/event、/api/openchamber/events、/api/notifications/stream、/api/openchamber/realtime-proxy/sse/api/fs/raw、/api/fs/serve及其子路径用于被 serve 的 HTML 页面自身的子资源/api/preview/proxy/前缀项目图标/api/projects/id/icon、/api/guests及 guest 文件路径WebSocket 白名单isUrlAuthWebSocketPath见 ui-auth.js包括/api/event/ws、/api/global/event/ws、/api/openchamber/realtime-proxy/ws、/api/terminal/ws、/api/dictation/ws、/api/dev-tunnel、guest surface ws 与 preview 代理。关键结论URL token 绝不开放给任意的/api/*只覆盖浏览器可读 GET / 实时路径的窄白名单。要新增此类路径必须同时修改 ui-auth.js 白名单并在 ui-auth.test.js 中补充对应测试且允许 guest scope 的令牌仅限 GET 且必须落在该 guest 自己的路径下canUseUrlAuthTokenForRequest见 ui-auth.js。一个值得一提的细节是/api/fs/serve/的Referer 令牌继承被 serve 的 HTML 页面用相对 URL 加载自己的图片、样式、脚本这些子资源请求本身不携带令牌但浏览器会把带令牌的页面 URL 作为同源子资源的Referer发送。服务端仅在页面与子资源都在/api/fs/serve/下时才从 Referer 提取令牌且照常校验有效性、过期与作用域见 ui-auth.js。四、展示别人的页面不改写只隧道关联文档明确指出OpenChamber 不会改写rewrite第三方 HTML 来展示它。改写页面并挂到我们的 origin 与路径前缀下会破坏页面上每一个绝对 URL要恢复就得编码对每个框架 dev-server 内部机制的了解——这既容易过时又会静默失败。4.1 Chromiumwebview应用内浏览器桌面端的应用内浏览器渲染真实的 Chromiumwebview实现位于 packages/ui/src/components/browser/。从 BrowserPane.tsx 的注释可以看到当 Chromium 宿主不可用时界面降级为普通 iframe——它能展示页面但无法检查页面内容。必须明确声明这一限制而不是绕过它模拟。4.2 Dev Server 隧道远程开发机页面的原生方案当 dev server 跑在远程 OpenChamber 主机上时正确做法是绑定本地端口 透传原始字节让页面在自己主机的根路径上保持自己的 origin。该模块位于 packages/web/server/lib/dev-tunnel/其DOCUMENTATION.md说明runtime.js是主机端接受/api/dev-tunnel的 WebSocket upgrade、完成鉴权、打开指向目标端口的 TCP socket 并双向 pipeclient.js是本地端在用户机器上绑定 loopback 监听端口把每个接入连接通过一个 WebSocket 透传relay-only 运行时则由 packages/electron/relay-dev-tunnel.mjs 承载本地监听每个连接一个 ElectronMessagePort由受信 renderer 通过 E2EE relay 搬运字节。页面决策层在 packages/ui/src/lib/browser/devTunnel.tsresolveBrowsableUrl判断是否需要隧道仅桌面运行时 远程运行时 loopback URL 时shouldTunnelLoopbackUrl处理页面自身发起的二次导航页内跳转到另一个本地端口仍应指向主机而非用户本机toDisplayUrl则把隧道随机本地端口映射回用户请求的原始地址避免地址栏与持久化状态里出现无意义的端口。隧道模块有三个严格不变量值得注意可达端口集 dev-server 发现机制提供给用户的集合绝不是任意 loopback 端口否则鉴权客户端可通过该 socket 拨号主机上的任意本地服务鉴权按调用方区分带Origin头走浏览器 origin 白名单CSRF 防护不带Origin则必须是 client-token桌面主进程或短时 URL tokenE2EE relay 场景且白名单只接受精确的/api/dev-tunnel不含子路径打不开的隧道必须报告给面板绝不能悄悄替换成普通 loopback URL——在远程实例上那会指向用户本机的同名端口展示的是另一台机器的内容。4.3 postMessage 与运行时切换两条补充规则同样重要不要使用postMessage(*)必须显式指定已知 origin防止任意页面接收敏感消息。运行时切换后必须重新解析浏览器 URL不能保留为旧运行时铸造的 URL。从 devTunnel.ts 可以看到subscribeRuntimeEndpointChanged(resetDevTunnelCache)在端点变更时清空整个隧道缓存并关闭所有 relay socket因为旧监听器属于上一端点继续持有其端口会把 URL 解析到用户已离开的主机。五、安全测试为白名单与令牌行为提供回归保障关联文档要求这些机制必须有聚焦的测试覆盖仓库中对应的测试文件为packages/ui/src/lib/runtime-url.test.ts验证相对/绝对 URL 构建、WebSocket 协议转换、本地代理路由等packages/ui/src/lib/runtime-auth.test.ts验证令牌铸造、原子替换、主动刷新与消费者计数packages/web/server/lib/ui-auth/ui-auth.test.js验证 URL token 的签发、校验、过期、作用域与白名单路径packages/web/server/lib/dev-tunnel/tunnel.test.js验证隧道鉴权、并发上限与失败行为。测试的意义在于白名单每增加一个路径都必须同时证明新路径可经 URL token 访问且任意/api/*不可经 URL token 访问把安全边界固化为可回归的约束。六、实践清单把这套模式用到自己的集成中先分类再编码能自己 fetch 的小资源走runtimeFetchblob() object URL浏览器必须拥有的 URL 走authenticatedAssetSSE / WebSocket 分别走sse(...)/websocket(...)。绝不手动拼令牌oc_url_token只能由 resolver 生成服务端见到oc_client_token查询参数一律剥离/拒绝。缓存要完整object URL 缓存键包含运行时身份、实体 ID、版本、渲染选项按条数与字节双重限界驱逐即revokeObjectURL。令牌要短命默认 60 秒 TTL铸造时携带作用域如guest:id仅白名单 GET / 实时路径可用永远不放进任意/api/*。第三方页面不改写桌面端用真实webview远程 dev server 走 dev-tunnel 保持原 origin无 Chromium 环境降级 iframe 并明示限制。切换运行时即重解析订阅端点变更事件清空旧运行时相关的 URL、缓存与隧道端口。安全边界要有测试任何白名单调整必须同步更新 ui-auth.test.js守住URL token 不可访问任意 API的底线。赞分享AI Agent人工智能代码智能体交互助手【免费下载链接】openchamberAgentic Development Environment based on OpenCode AI agent项目地址https://gitcode.com/gh_mirrors/op/openchamber点击查看免费下载相关推荐Web-Dev-For-Beginners 实战项目Carbon Trigger 浏览器扩展完整实现指南源码解读与加载部署Web Dev For Beginners 实战项目Carbon Trigger 浏览器扩展完整实现指南源码解读与加载部署 本篇技术指南基于 Web De文档教程前端Ice 上手记macOS 菜单栏图标从 14 个压到 6 个需要时一键呼出Ice 上手记macOS 菜单栏图标从 14 个压到 6 个需要时一键呼出 早上连着开三个会Slack、Zoom、日历、剪贴板小工具……菜单栏右半边被挤得桌面应用Socket.IO 与 Express Passport 鉴权集成在握手阶段复用浏览器会话的完整实践Socket.IO 与 Express Passport 鉴权集成在握手阶段复用浏览器会话的完整实践 Socket.IO 的连接是独立于 HTTP 页面的后端即时通讯WebSocket上一篇B站下载工具终极指南BiliTools哔哩哔哩工具箱完整使用教程下一篇技术解析Download Full Installer的macOS安装包管理架构实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表