# 业务后端怎样让用户安全入会 第三方业务后端知道自己的用户和会议成员关系。它先校验业务用户,再用保存在服务端的 AKStream.Next API Token 建立房间或签发一次性入会票据。浏览器和 App 只接收本人的票据,不接收管理员密码或长期 API Token。 ```mermaid flowchart LR U["业务用户登录你的系统"] --> B["业务后端核对会议资格"] B --> A["AKStream.Next 签 accessTicket"] A --> C["指定设备调用 RTC SDK join"] C --> T["SDK 持有并续签当前设备 RTC Token"] ``` ## 后端要做的三步 1. 按你的业务规则决定用户是否能进入这个会议,以及是主持人、发言人还是只看成员。 2. 用只存在于后端的 API Token 创建或选择房间;`mediaServerId` 必须指向实际已登记的媒体节点。 3. 通过 `POST /api/v2/rtc/rooms/{roomId}/access-tickets` 为该用户签票,将响应里的 `roomId` 和 `accessTicket` 只交给这一次入会的设备。 请求示例(`subjectId` 应取自**后端已验证**的业务用户,不接受客户端自行决定): ```http POST /api/v2/rtc/rooms/{roomId}/access-tickets Authorization: Bearer <后端保管的 API Token> Content-Type: application/json {"subjectIssuer":"business-app","subjectId":"user-1001","displayName":"张三","role":"viewer","capabilities":["publications.subscribe"],"expiresInMinutes":5,"bypassWaitingRoom":false} ``` 这是只看成员。需要发布麦克风或摄像头时,由业务后端根据资格改为相应角色并请求 `microphone.publish`、`camera.publish` 能力;服务端仍会按房间策略取交集。一次性票据有期限且只能消费一次,失败重试时先判断票据是否已经被消费,再向后端申请新票。 如果业务后端使用 Node.js 的标准 `fetch`,下面的函数可以放在已验证业务登录的服务端路由中。`user.id` 与 `canSpeak` 必须由服务端得出,不能照收网页传来的角色。运行前在服务端设置 `AKSTREAM_URL` 与 `AKSTREAM_API_TOKEN`: ```js export async function issueRtcTicket(roomId, user, canSpeak) { const base = process.env.AKSTREAM_URL const token = process.env.AKSTREAM_API_TOKEN if (!base || !token || !roomId || !user?.id) throw new Error('缺少服务配置或已验证的用户') const response = await fetch(new URL(`/api/v2/rtc/rooms/${encodeURIComponent(roomId)}/access-tickets`, base), { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ subjectIssuer: 'business-app', subjectId: String(user.id), displayName: String(user.displayName || user.id), role: canSpeak ? 'speaker' : 'viewer', capabilities: canSpeak ? ['microphone.publish', 'camera.publish', 'publications.subscribe', 'chat.send'] : ['publications.subscribe'], expiresInMinutes: 5, bypassWaitingRoom: false }), signal: AbortSignal.timeout(15000) }) if (!response.ok) throw new Error(`AKStream.Next 签票失败:HTTP ${response.status}`) const ticket = await response.json() return { roomId, accessTicket: ticket.accessTicket, expiresAtUtc: ticket.expiresAtUtc } } ``` 先用普通只看用户调用,确认返回的 `roomId` 与请求一致且票据非空;随后在客户端入会。401 查服务端 API Token,403 查 `rtc.manage` 与该房间的业务授权,429 按限流结果等待,不要反复签票。日志只记状态码和业务请求 ID,不记录完整 `accessTicket`。 ## 客户端拿到票后 调用目标端 SDK 的 `join`/`joinWithAccessTicket`,由 SDK 处理参会令牌续签、信令与媒体会话。返回 `Waiting` 时停在等候页面,不自行发布;主持准入后根据 SDK 事件继续。`participantId` 指当前会议成员,`deviceSessionId` 指一次设备会话,同一用户多设备不能按姓名合并成一台。 服务器需要终端可达的 HTTPS 地址和媒体端口,证书必须与设备实际访问的域名或 IP 匹配。网页能打开不等于 ICE 媒体可通。设置方法见端口、域名和 HTTPS。 进入会议后页面从 SDK 读取状态,不保存或解码 RTC Token。用户离会时调用 SDK `leave`;只关闭网页或 Activity 不能保证服务器立即释放设备会话。下一步按[端类型](home.md)选择接入指南。