控制谁能连接和订阅 沿用您现有的会话规则。
向浏览器和移动客户端推送私有订单更新、账户提醒和工作区事件,无需在客户端代码中暴露后端凭据。您自己的鉴权端点会检查调用方的会话,并按连接为私有或在线状态频道订阅签名;端到端加密则在敏感载荷离开您的服务器之前将其加密。
auth.ts
200 · 已签名403 · 禁止访问200 · 已签名200 · 已签名// Your endpoint. The only place the app secret lives.app.post("/bird/auth", async (req, res) => { const { connection_id, channel_name } = req.body; const user = await session(req); if (!mayJoin(user, channel_name)) return res.sendStatus(403); // Signs <connection_id>:<channel_name>[:<member_data>] with the app // secret, and adds shared_secret on an encrypted channel. res.json( await bird.realtime.authorizeChannel({ connectionId: connection_id, channelName: channel_name, memberData: JSON.stringify({ member_id: user.id, member_info: { name: user.name }, }), }), );});
POST/bird/auth
响应正文
{ "auth": "d0e95a856ddc1b09d4c8:8f9a2c71e4…", "member_data": "{\"member_id\":\"u_42\",\"member_info\":{\"name\":\"Maya Chen\"}}" }
auth.ts
200 · 已签名403 · 禁止访问200 · 已签名200 · 已签名// Your endpoint. The only place the app secret lives.app.post("/bird/auth", async (req, res) => { const { connection_id, channel_name } = req.body; const user = await session(req); if (!mayJoin(user, channel_name)) return res.sendStatus(403); // Signs <connection_id>:<channel_name>[:<member_data>] with the app // secret, and adds shared_secret on an encrypted channel. res.json( await bird.realtime.authorizeChannel({ connectionId: connection_id, channelName: channel_name, memberData: JSON.stringify({ member_id: user.id, member_info: { name: user.name }, }), }), );});
POST/bird/auth
响应正文
{ "auth": "d0e95a856ddc1b09d4c8:8f9a2c71e4…", "member_data": "{\"member_id\":\"u_42\",\"member_info\":{\"name\":\"Maya Chen\"}}" }
auth.ts
200 · 已签名403 · 禁止访问200 · 已签名200 · 已签名// Your endpoint. The only place the app secret lives.app.post("/bird/auth", async (req, res) => { const { connection_id, channel_name } = req.body; const user = await session(req); if (!mayJoin(user, channel_name)) return res.sendStatus(403); // Signs <connection_id>:<channel_name>[:<member_data>] with the app // secret, and adds shared_secret on an encrypted channel. res.json( await bird.realtime.authorizeChannel({ connectionId: connection_id, channelName: channel_name, memberData: JSON.stringify({ member_id: user.id, member_info: { name: user.name }, }), }), );});
POST/bird/auth
响应正文
{ "auth": "d0e95a856ddc1b09d4c8:8f9a2c71e4…", "member_data": "{\"member_id\":\"u_42\",\"member_info\":{\"name\":\"Maya Chen\"}}" }
向客户端分发公钥
签名密钥始终保留在您的服务器上。
Bird Realtime API 上的应用将客户端连接凭据与后端权限分离。浏览器和移动应用使用公开的应用密钥打开 WebSocket,该密钥可安全嵌入客户端包中;您的后端则持有用于发布事件和签名订阅令牌的密钥。当安全策略要求轮换凭据时,您可以创建第二组密钥对,在各服务中逐步部署,然后吊销旧密钥对,而不会断开活跃用户。
在每一层保护连接、频道和载荷
验证谁能打开套接字,授权其加入哪些频道,加密敏感事件数据,并在会话结束时切断访问。
- 01
签名的频道订阅。
当浏览器或移动客户端订阅私有或在线状态频道时,您的后端会检查用户会话,并签发一个与该连接 ID 和频道名称绑定的 HMAC-SHA256 令牌。签名与套接字 ID 绑定,令牌就无法在其他连接上重放;在线状态签名还会锁定向房间展示的成员身份。
- 02
经过身份验证的 WebSocket 连接。
由于公开的应用密钥位于客户端代码中,任何访问者都可能尝试打开空闲的 WebSocket。启用授权连接后,新套接字必须在 30 秒内通过您的后端完成身份验证,方式是登录或订阅私有频道。未验证的套接字会自动关闭,且不计入您的连接配额。
- 03
端到端加密载荷。
对于受监管或机密的更新,服务器端 SDK 使用保留在您基础设施上的 32 字节主密钥在本地加密事件载荷。每个加密频道派生各自的解密密钥,因此批准客户端访问某个机密流并不会授予其访问其他流的权限,中间服务器在传输过程中只能看到密文。
- 04
即时吊销会话。
当用户退出登录、修改密码或失去工作区访问权限时,只需一次服务器调用,即可在所有打开的浏览器标签页和移动设备上断开其成员身份的连接。客户端会将此终止视为最终结果,而不会循环重连;会话吊销后,您的鉴权端点可以拒绝之后的登录尝试。
加密频道
在敏感载荷离开您的服务器之前将其加密。
在服务器端 SDK 中配置 32 字节主密钥,即可在发布前于您自己的进程内加密机密事件。Bird 将加密后的封装路由给订阅者,您的授权端点则把频道派生的解密密钥发送给获批的浏览器客户端。您还可以将加密与最新事件缓存结合使用,让新连接的客户端以密文形式收到最新状态。由于路由元数据不加密,频道名和事件名应保持通用。
const bird = new BirdClient({
apiKey: process.env.BIRD_API_KEY!,
realtime: {
key: process.env.BIRD_REALTIME_KEY,
secret: process.env.BIRD_REALTIME_SECRET,
// 32 random bytes, yours alone. Never sent to Bird.
encryptionMasterKey: process.env.BIRD_REALTIME_MASTER_KEY,
},
});
// Sealed in your process. The edge forwards ciphertext.
await bird.realtime.publish(APP_ID, {
event: "order.updated",
channels: ["private-encrypted-orders"],
data: { order_id: "ord_123", status: "shipped" },
});
隔离租户数据流,并在服务器上校验状态。
连接身份验证和频道授权分别解决访问控制的不同部分。要求授权连接可阻止匿名套接字,而私有和在线状态频道则限定哪个已登录用户或租户可以读取特定数据流。请将客户专属更新放在经您的鉴权端点验证的私有频道上,将客户端间事件用于输入提示等临时界面信号,并通过您的后端处理会改变状态的操作。
将流量固定在某个区域,并按应用隔离环境。
创建 Realtime 应用时选择专用区域,即使您的工作区主区域在别处,也能让 WebSocket 连接和已发布事件靠近您的用户。每个应用都在独立的隔离命名空间中运行,拥有各自的凭据和配额,因此为开发、预发布和生产环境分别创建应用,可避免测试广播进入线上客户频道。
继续构建
使用 Realtime。
使用同一套应用凭据,将频道授权与实时在线状态、状态缓存和后端 webhook 结合使用。
规模扩展
不失控。
在工作区中组织团队,控制 API 访问权限,通过审计日志追踪变更。
组织
Harbor
访问权限仅限于其所在工作区。
生产环境/API 密钥
Harbor 后端
活跃Alex Lee
管理员
今天 · UTC
审计日志
组织
Harbor
访问权限仅限于其所在工作区。
生产环境/API 密钥
Harbor 后端
活跃Alex Lee
管理员
今天 · UTC
审计日志
组织
Harbor
访问权限仅限于其所在工作区。
生产环境/API 密钥
Harbor 后端
活跃Alex Lee
管理员
今天 · UTC
审计日志