WeKnora Embed 安全模式部署指南:发布 Token 服务端化与短时会话令牌机制详解 WeKnora Embed 安全模式部署指南发布 Token 服务端化与短时会话令牌机制详解【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora本指南面向需要在生产环境对外嵌入 WeKnora 聊天 Widget 的开发者完整讲解如何通过「安全模式」把长期发布 Tokenem_…隔离在自有服务端让访客浏览器只持有 30 分钟有效的短时会话令牌ems_…。读完本文你将掌握两种令牌的职责与生命周期、exchange交换接口的完整调用约定、Node.js / Go 两种服务端取令牌接口的落地代码、域名白名单的精确配置方法以及上线前必须逐项核对的安全检查清单。为什么必须使用安全模式WeKnora 的 embed 能力允许你把知识库问答 / Agent 对话以 iframe 或浮动 Widget 的形式嵌入任意第三方网站。但「能嵌入」不等于「可以随便嵌」——嵌入方式直接决定了你的长期凭证是否暴露在公网。方式发布 Token 在哪风险iframe / 普通 Widget写在页面 HTML 或 URL hash 里任何人「查看源代码」就能复制等于公开密钥安全模式 Widget仅环境变量 / 密钥管理在服务端浏览器拿不到长期密钥还可先校验访客是否登录普通模式下data-token或token参数携带的长期发布 Token 会随 HTML 下发到每个访客的浏览器。发布 Token 的权限是渠道级别的持有它的人可以换取会话令牌、调用聊天与上传等 embed API。一旦泄漏任何人都能冒充你的渠道消耗配额甚至滥用对话能力。因此生产环境对外嵌入应优先用安全模式。安全模式的核心思路是把「换取令牌」这件事从浏览器移到你自己的服务端浏览器只拿 30 分钟有效的短时令牌长期令牌永远不出服务端。两种 Token 的职责与生命周期安全模式涉及两种完全不同粒度的凭证二者格式、持有方、用途与生命周期都不同名称格式谁持有用途发布 Tokenem_…仅你的服务端向 WeKnora 换取短时令牌在管理端「渠道密钥」查看会话 Tokenems_…访客浏览器iframe 内调聊天、上传等 embed API约 30 分钟过期Widget 会自动刷新从源码可以确认二者的硬性区分会话令牌以ems_前缀存储于 RedisTTL 固定为30 * time.Minute见 internal/application/service/embed_session.go每次签发都是crypto/rand生成的 32 字节随机值通过embed:session:token键映射到渠道 ID见 IssueSessionToken。更重要的是服务端强制拒绝用会话令牌换新令牌。在ExchangeEmbedSession处理器中有明确注释只接受长期发布令牌来铸造会话令牌否则持有会话令牌的人可以无限续期、永远不必重新出示发布令牌见 internal/handler/embed_channel.go。这就是ems_…无法升级、无法续期的根本原因。安全模式工作流程整个链路涉及三方访客浏览器、你的业务后端、WeKnora 服务端访客浏览器 你的后端shop 的服务器 WeKnora │ │ │ │ 1. 加载 Widget │ │ │ >POST https://weknora-host/api/v1/embed/channel_id/exchange Authorization: Embed 发布 Token em_… Origin: https://你的业务站点 ← 须与渠道白名单一致否则 403服务端fetch默认不带Origin需要手动设置与白名单匹配的Origin头。exchange 端点定义在公开路由组中见 internal/router/routes_agent.go整个/api/v1/embed/:channel_id组都由EmbedAuth中间件保护不经过全局登录鉴权凭令牌 Origin 限流三重校验放行。成功响应体为{ success: true, data: { session_token: ems_…, expires_in: 1800 } }注意响应字段名令牌字段是session_token过期秒数是expires_in你的服务端在透传给前端时再转换为{ token, expiresIn }的驼峰结构。第 3 步粘贴安全模式 Widget 代码把data-token-endpoint改成上一步的真实 URL。完整示例在管理端「安全模式」Tab 可复制。Widget 同时支持编程式初始化WeKnora.init({ channel, tokenEndpoint, ... })与脚本标签的data-*属性两种方式安全模式下一律传tokenEndpoint/data-token-endpoint而非token/data-token见 weknora-widget.js。服务端取令牌示例以下WEKNORA_HOST、CHANNEL_ID替换为实际值发布 Token 放环境变量WEKNORA_PUBLISH_TOKEN不要写进前端。Node.jsExpressconst WEKNORA_BASE https://WEKNORA_HOST; const CHANNEL_ID CHANNEL_ID; const ALLOWED_ORIGIN https://shop.example.com; // 与渠道白名单一致 app.get(/weknora/embed-token, async (req, res) { const hasSession Boolean(req.cookies?.session_id); const auth req.headers.authorization || ; if (!hasSession !auth.startsWith(Bearer )) { return res.status(401).json({ error: unauthorized }); } const r await fetch(${WEKNORA_BASE}/api/v1/embed/${CHANNEL_ID}/exchange, { method: POST, headers: { Authorization: Embed process.env.WEKNORA_PUBLISH_TOKEN, Origin: ALLOWED_ORIGIN, }, }); const body await r.json(); if (!body?.data?.session_token) { return res.status(502).json({ error: mint failed }); } res.json({ token: body.data.session_token, expiresIn: body.data.expires_in }); });Gonet/httpfunc embedTokenHandler(w http.ResponseWriter, r *http.Request) { if r.Header.Get(Authorization) r.Header.Get(Cookie) { http.Error(w, {error:unauthorized}, http.StatusUnauthorized) return } req, _ : http.NewRequest(http.MethodPost, https://WEKNORA_HOST/api/v1/embed/CHANNEL_ID/exchange, nil) req.Header.Set(Authorization, Embed os.Getenv(WEKNORA_PUBLISH_TOKEN)) req.Header.Set(Origin, https://shop.example.com) // 与渠道白名单一致 resp, err : http.DefaultClient.Do(req) if err ! nil || resp.StatusCode 300 { http.Error(w, {error:mint failed}, http.StatusBadGateway) return } defer resp.Body.Close() var body struct { Data struct { SessionToken string json:session_token ExpiresIn int json:expires_in } json:data } if json.NewDecoder(resp.Body).Decode(body) ! nil || body.Data.SessionToken { http.Error(w, {error:mint failed}, http.StatusBadGateway) return } w.Header().Set(Content-Type, application/json) json.NewEncoder(w).Encode(map[string]any{ token: body.Data.SessionToken, expiresIn: body.Data.ExpiresIn, }) }管理端「安全模式 → 服务端示例」Tab 会按当前渠道 ID 生成带真实 URL 的片段可直接复制改造。域名白名单怎么填渠道的「域名白名单」校验的是每个 API 请求的Origin头无 Origin 时回退取Referer的 scheme host不是「允许哪些网站粘贴脚本」的抽象概念。中间件中的匹配逻辑见 internal/middleware/embed_auth.go精确匹配、*通配全部、*.example.com后缀通配三种形式且白名单为空时拒绝一切来源。要放行的请求来源白名单示例聊天 iframe 所在源站embed 页面https://app.example.com或https://embed.example.com你的取令牌后端exchange 时带的 Originhttps://shop.example.com需要注意两类请求的 Origin 来源不同聊天 iframe 内发 API浏览器自动携带 iframe 所在页面的 Origin即 embed 页面源站服务端 exchange服务端代码手动设置的Origin头Node/Go 示例中的ALLOWED_ORIGIN若 embed 使用独立子域两条都要加embed 源站 业务后端源站。而第三方宿主站如https://shop.example.com本身只是加载脚本、不直接调 embed API通常不用进白名单。开发环境可临时使用*生产环境禁止*。这一点在服务端有硬校验渠道创建 / 更新时validateAllowedOrigins会拒绝空白名单并在GIN_MODErelease的生产模式下直接拒绝*通配见 internal/handler/embed_channel.go白名单不是建议而是强制要求。更进一步的防护机制限流三层叠加安全模式并未让渠道免于滥用风险——发布 Token 虽不出现在浏览器但会话令牌仍可被批量领取。因此EmbedAuth中间件对每个请求叠加三层限流见 internal/middleware/embed_auth.go按 IP 每分钟rate_limit_per_minute配置值key 为channelID:客户端IP渠道全局每分钟由 per-IP 值推导perIP × 20下限 120用于遏制攻击者换 IP 绕过单 IP 限制渠道全局每日rate_limit_per_day配置值遏制持续滥用会话签名防越权访问嵌入会话创建后服务端会签发一个 HMAC 签名key 为渠道的发布 TokenWidget 在每次历史加载 / 聊天调用时必须通过X-Embed-Session头携带。即便会话 ID 从访问日志泄漏没有对应签名也无法操作该会话见 internal/handler/embed_channel.go 与 embed_session.go。渠道级能力裁剪EmbedAuth之外每个 embed 聊天请求还会被强制改写渠道未开启allow_web_search时关闭联网搜索、未开启allow_file_upload时剥离上传字段、固定使用渠道绑定的agent_id见 internal/handler/embed_channel.go。这保证访客只能在渠道管理员配置的能力范围内对话。上线检查清单发布 Token 仅通过环境变量 / 密钥服务注入未提交到 Git、未打进前端静态包取令牌接口校验访客身份Session / JWT未登录返回401全链路 HTTPS业务站点 → 取令牌接口 → WeKnora 均需 TLS白名单已包含 embed 源站与 exchange 使用的 Origin已配置限流敏感智能体不要用普通模式把 Token 暴露在网页里轮换发布 Token 后同步更新服务端环境变量渠道管理端提供rotate-token接口常见问题排查现象原因与处理exchange 返回401/publish token required发布 Token 错误、已轮换或误用了ems_会话 Token。会话令牌在 exchange 路径上会被显式拒绝见 embed_channel.goexchange 或聊天 API 返回403origin not allowed白名单未包含当前请求的Origin服务端 exchange 记得手动加Origin头。Origin为空时中间件同样拒绝iframe 一直「等待 Token」token-endpoint未返回{ token, expiresIn }或 CORS 未允许 Widget 所在源站访问你的接口取令牌接口502mint failedWeKnora 不可达、渠道已停用或 exchange 响应格式不对字段应为data.session_token/data.expires_in访客随便就能聊取令牌接口未做登录校验——在 exchange 前加 Session / JWT 检查相关阅读可选进阶部署embed 独立子域隔离embed.example.com与主站分离见 docs/embed-subdomain.mdWidget SDK 注释与安全模式实现frontend/public/weknora-widget.js前端 embed API 代码生成frontend/src/api/embed/index.ts服务端公开路由与鉴权中间件internal/router/routes_agent.go、internal/middleware/embed_auth.go会话令牌签发与校验实现internal/application/service/embed_session.go渠道 CRUD、exchange、会话签名处理器internal/handler/embed_channel.go【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考