JSSDK 配置与签名
阅读指引:本篇是《开放平台服务端 API 指南》的第 8 部分。接口基础地址、通用约定与各接口请求限制,请先阅读《服务端 API 概览》。
11. JSSDK config 与受保护能力
JSSDK 已提供 ready、error、getAuthCode、config、closeWindow、chooseChat 和 openChat。getAuthCode/closeWindow 在 Bridge ready 后可直接调用;chooseChat/openChat 是受保护能力,必须先完成包含目标 JSAPI 的 config。
| 生命周期 API | 参数 | 触发时机 |
|---|---|---|
ready(callback) |
callback: () => void,必填 |
未调用 config 时,在 Bridge 初始化成功后触发;页面调用 config 时,在 Bridge 和 config 都成功后触发。应在其他 JSAPI 之前注册。 |
error(callback) |
callback: (error) => void,必填 |
Bridge 初始化或 config 失败时触发;error 对象结构见第 11.6 节。应与 ready 一起尽早注册。 |
适用客户端类别为 Android、iOS 和 PC;实际可用能力以平台交付的客户端及 SDK 版本为准。JSSDK 最低版本为 1.0.0,Bridge 最低版本为 1.0。能力调用前仍会按当前客户端版本检查支持情况。
11.1 应用后端获取 jsapi_ticket
POST /openapi/v1/jssdk/ticket
curl -X POST "${OPENAPI_BASE_URL}/openapi/v1/jssdk/ticket" \
-H "Authorization: Bearer ${APP_ACCESS_TOKEN}"
{
"ticket": "jst_mim_xxx",
"expires_in": 7200
}
| 位置/字段 | 类型/格式 | 必填 | 含义、范围与单位 |
|---|---|---|---|
Header Authorization |
Bearer <app_access_token> |
是 | 有效的 App access token;User access token 不可使用。 |
| 请求 body | 无 | 否 | 该接口不接收 JSON 或表单参数。 |
ticket |
string | 成功时 | 短期 JSSDK 签名密钥,只能保存在应用后端并按不透明字符串使用。 |
expires_in |
integer | 成功时 | 从响应生成时刻起的剩余有效期,单位秒;最长通常为 7200 秒,也不会超过来源 App access token 的剩余有效期。 |
有效期充足时,同一应用重复调用会复用当前 ticket;临近过期时平台轮换 ticket,并在短暂过渡期内接受仍有效的上一张 ticket。应用后端应按 expires_in 缓存,不要为每个页面请求一次 ticket。响应带有 Cache-Control: no-store 和 Pragma: no-cache,失败时返回 JSON 业务码而不返回 ticket。
请求体最大 1 MiB但不接收业务参数;同一应用与来源 IP 每分钟最多调用 60 次,并同时受来源 IP 每分钟 300 次的全局限制。
| 失败业务码 | 含义与处理 |
|---|---|
170104 |
App access token 无效、过期、已撤销或 Token 类型错误;重新获取应用 Token。 |
170103 |
应用不存在、未发布或当前不可用;联系管理员检查应用状态。 |
170108 |
请求频率超限;按限流窗口退避后重试。 |
170309 |
ticket 服务异常;记录排障信息并有限次数重试。 |
服务端专用app_secret、app access token 和 jsapi_ticket 都不能进入 H5、客户端包体、普通日志或错误响应。
11.2 应用后端计算签名
import crypto from "node:crypto"
const jsApis = [...new Set(["openChat"].map(v => v.trim()).filter(Boolean))].sort()
const signedUrl = pageUrl.trim().split("#")[0] // 保留 query 原顺序,不重编码
const canonical = `v=1&app_key=${appKey.trim()}` +
`&js_api_list=${jsApis.join(",")}` +
`&nonce=${nonce.trim()}×tamp=${timestamp}&url=${signedUrl}`
const signature = crypto
.createHmac("sha256", jsapiTicket)
.update(Buffer.from(canonical, "utf8"))
.digest("hex")
签名字符串必须严格按示例字段名和顺序构造,UTF-8 编码,字段值不额外 URL 编码,末尾不加换行。jsApiList 先 trim、去空、去重,再按区分大小写的字符串升序排序并用英文逗号连接;URL 只去掉 fragment,必须保留 query 的原值和顺序。
11.3 H5 调用 config
im.openPlatform.ready(() => {
// config 成功且 Bridge ready 后触发。
})
im.openPlatform.error(err => console.error(err.code, err.message))
await im.openPlatform.config({
appKey: "svc_app_xxx",
timestamp: 1787366400000,
nonce: "nonce_20260822_001",
signature: "b4be79f21080926eb10e7c3f81a50090a897a2731a10ccfbf9cf9d18a2389e2c",
url: location.href.split("#")[0],
jsApiList: ["openChat"]
})
| 参数 | JavaScript 类型 | 必填 | 含义、范围与单位 |
|---|---|---|---|
appKey |
string | 是 | 应用公开标识,与获取 App access token 时使用的 app_key 相同。 |
timestamp |
number | 是 | 应用后端生成签名时的 Unix 毫秒时间戳;平台只接受服务器当前时间前后各 5 分钟。 |
nonce |
string | 是 | 应用后端生成的随机串,trim 后非空且最长 128 字节;签名认证成功后同一应用不能重放相同 nonce。 |
signature |
string | 是 | 应用后端使用当前 jsapi_ticket 计算的 64 位小写 hex HMAC-SHA256。 |
url |
string | 是 | 参与签名的当前页面主 Frame URL;使用 location.href.split("#")[0],保留 query 及其顺序,并确保签名值与调用 config 时的页面 URL 完全一致。 |
jsApiList |
string[] | 是 | 本页需要启用且要求 config 的 JSAPI;已登记 openChat 和 chooseChat。任一名称未知、未授权或不要求 config 时,整个 config 失败。 |
chooseChat 用于选择用户会话列表,选择数量由用户自行决定;对应应用权限默认授予,当前状态为可用。调用前须完成 ready/config,获得 chat:conversation:choose,并由已登录用户的点击触发。该选择器由客户端 SDK 提供;具体入参和返回对象须使用平台交付 SDK 的类型定义,不得自行假设返回字段或用组织用户 ID 代替会话 ID。
config 成功后,本页只能调用 jsApiList 中已批准的受保护 JSAPI。配置过期、页面 URL 变化或应用权限变化后,应重新取得签名并调用 config;具体能力仍会校验当前用户的资源访问权和必要的用户手势。
JSSDK config 用于启用客户端能力,不应当作应用后端的用户登录凭据或业务授权证明。后端识别当前用户时须完成 OAuth2 换码并验证用户 Token 对应的身份,不能信任 H5 自报的用户信息。
11.4 closeWindow
await im.openPlatform.closeWindow()
closeWindow() 无参数,需要 Bridge ready,不需要 config、Scope 或用户登录上下文。Promise resolve 表示关闭请求已由容器接受;它只能关闭当前应用 WebView,不能指定或关闭其他窗口。
11.5 openChat
await im.openPlatform.openChat({
conversationId: "conversation_xxx"
})
// 只打开当前用户有权访问的会话;不读取消息,也不代替用户发送消息。
| 参数/结果 | 类型 | 必填 | 说明 |
|---|---|---|---|
conversationId |
string | 是 | 需要打开的会话不透明标识;不得解析、枚举或用其他类型的 ID 替代。 |
| 用户手势 | 运行条件 | 是 | 必须由用户本次点击等可信交互直接触发,不能在定时器、页面加载或后台回调中自动调用。 |
| Promise resolve | void |
成功时 | 表示客户端已接受并完成会话导航,不返回消息内容或成员资料。 |
调用前必须满足:Bridge ready;当前 config 尚未过期且 jsApiList 包含 openChat;管理员已授予 chat:conversation:open;当前登录用户仍有权访问目标会话。每次调用都会重新检查用户手势和会话访问权。
11.6 统一 JSSDK 错误
所有返回 Promise 的 JSAPI 失败时 reject 以下对象;Bridge 初始化或 config 失败还会触发通过 error(callback) 注册的回调。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
code |
string | 是 | 稳定的机器可读错误码;应用应据此分支处理。 |
message |
string | 是 | 面向开发排障的简短英文说明,不保证长期逐字不变,不应直接展示给最终用户。 |
requestId |
string | 否 | 存在服务端调用时的排障请求标识;纯本地失败可能省略。 |
details |
object | 否 | 非敏感补充信息;字段可扩展,业务逻辑不得依赖未文档化成员。 |
code |
含义与建议处理 |
|---|---|
SDK_NOT_READY |
Bridge 尚未 ready;等待 ready 回调后再调用。 |
UNSUPPORTED_ENVIRONMENT / BRIDGE_VERSION_UNSUPPORTED |
不在受支持的应用容器中或客户端版本过低;提示用户升级或在受支持客户端打开。 |
CONFIG_REQUIRED / CONFIG_EXPIRED / JSAPI_NOT_CONFIGURED |
缺少有效 config;重新获取签名并 config,确保列表包含目标 JSAPI。 |
CONFIG_INVALID / INVALID_SIGNATURE / UNTRUSTED_URL |
参数、签名、nonce、时间或页面 URL 不匹配;重新生成整组参数,不复用旧 nonce。 |
JSAPI_NOT_FOUND / JSAPI_NOT_SUPPORTED |
名称错误或当前客户端不支持;检查拼写和客户端版本。 |
JSAPI_PERMISSION_DENIED |
应用缺少 Scope、状态不可用或能力未授权;联系管理员检查应用权限。 |
USER_NOT_AUTHENTICATED |
当前容器没有有效登录用户;要求用户登录后重试。 |
USER_GESTURE_REQUIRED |
调用不是由可信用户操作直接触发;移到点击事件处理函数中。 |
RESOURCE_ACCESS_DENIED / RESOURCE_NOT_FOUND |
用户无权访问目标资源,或资源不存在/不可见;不要循环重试。 |
USER_CANCELLED / SYSTEM_PERMISSION_DENIED |
用户取消或拒绝系统权限;尊重用户选择并允许稍后主动重试。 |
INTERNAL_ERROR |
客户端或服务端异常;记录 requestId 并有限次数重试。 |