跳到主要内容

登录验证

游戏客户端在调用 Combo SDK 的 Login() 成功完成登录后,会得到 Identity Token

Identity Token 是世游服务端签发的一个 JWT,包含了客户端的可信身份信息。

游戏客户端应当将 Identity Token 发送给游戏服务端,由游戏服务端对其做登录验证,并从中获得游戏客户端的身份信息。

签名算法

Identity Token 是用 HS256 算法进行签名的。

签名的私钥是世游给游戏分配的 Secret Key,游戏服务端需要用它验证 JWT 的签名。

Claims

Identity Token 的 payload 中会包含的 claim 如下:

ClaimTypeDescription
issstring颁发 JWT 的服务器,即世游 REST API 的端点。
audstring世游分配的游戏 ID,也就是 Game ID,用于标识游戏项目。
substring用户 ID。使用世游分配的聚合用户 ID,也就是 Combo ID。
iatintegerJWT 颁发时间。Unix timestamp in seconds。
expintegerJWT 的过期时间。Unix timestamp in seconds。
scopestringJWT 的作用域,固定为 auth
idpstring世游定义的 Identity Provider (IdP) 标识。取值参见 Go SDK 中的 IdP 常量定义
external_idstringidp 对应的外部身份的标识。
external_namestringidp 对应的外部身份中的全名,用于展示在 UI 上。
weixin_session_keystring微信小游戏登录的 session_key。仅在 idp 为 minigame_weixin 时才有值。此字段为加密字段,游戏服务端需解密后使用。
distrostring游戏客户端的发行版本标识。游戏侧可将它用于服务端数据埋点。
variantstring游戏客户端的分包标识。仅在客户端是分包时才有值。游戏侧可将它用于服务端数据埋点。
ageinteger根据用户的实名认证信息得到的年龄。0 表示未知。
device_idstring游戏客户端运行设备的唯一 ID,用于标识设备。
reg_timeinteger用户 ID(Combo ID)注册时间。Unix timestamp in seconds。

session_key 的使用

session_key 是微信小游戏颁发的会话密钥,游戏服务端可用其解密客户端从微信 API 获取的加密用户数据,或验证微信返回数据的签名完整性。详见微信官方文档:用户数据的签名验证和加解密

获取与缓存:客户端登录成功后,服务端应从 Identity Token 中解密 weixin_session_key,并将得到的 session_key 与用户会话关联缓存,以供后续使用。每次客户端重新调用 Login 时都会生成新的 session_key,服务端应及时更新缓存。

失效处理:若使用 session_key 时出现解密失败或签名验证失败,应视为 session_key 已失效,此时需引导客户端重新调用 Login 获取新的 session_key

危险

session_key 具有时效性,应仅在当前登录会话中使用,不应长期缓存或跨会话复用。

session_key 必须严格保存在游戏服务端,不得通过任何方式传回给客户端。一旦泄露,攻击者可用其伪造或篡改用户数据。

示例

假定:

  • 世游 API 端点 https://api.seayoo.com
  • Game ID 为 xcom
  • Secret Key 为 sk_secret

则一个长期有效的,具有合法 Claims 与 Signature 的 Identity Token 示例是:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FwaS5zZWF5b28uY29tIiwic3ViIjoiOTEyMzEyMjMzNDYxMzAwMDEiLCJhdWQiOiJ4Y29tIiwiZXhwIjo0MTAzODM0ODM3LCJpYXQiOjE3MDM3NDg0MzcsInNjb3BlIjoiYXV0aCIsImlkcCI6InNlYXlvbyIsImV4dGVybmFsX2lkIjoiMTk5OSIsImV4dGVybmFsX25hbWUiOiIqKuWzsCIsImRpc3RybyI6ImFuZHJvaWQiLCJ2YXJpYW50IjoiaHlrYiIsImFnZSI6MTYsImRldmljZV9pZCI6ImU0NmFlZjNjZmE1NzE5NTUiLCJyZWdfdGltZSI6MTcwMzMyNDIxM30.gaoHOMBo5tcLwBFURQ7IsAr1m-xx01HlRQEG9DYQKTI

这个 Identity Token 解码出的 Claims 为:

{
"iss": "https://api.seayoo.com",
"aud": "xcom",
"sub": "91231223346130001",
"iat": 1703748437,
"exp": 4103834837,
"scope": "auth",
"idp": "seayoo",
"external_id": "1999",
"external_name": "**峰",
"distro": "android",
"variant": "hykb",
"age": 16,
"device_id": "e46aef3cfa571955",
"reg_time": 1703324213
}
游戏侧可用 age 来自行处理防沉迷

age 不保证返回精确的年龄信息,仅保证用于防沉迷处理时的准确度够用。例如:

  • 当某个用户真实年龄为 35 岁时,age 可能返回 18
  • 当某个用户真实年龄为 17 岁时,age 可能返回 16

加密字段

Identity Token 中部分敏感字段在写入 JWT 前会经过加密处理,以防止敏感数据在传输链路中被直接读取。目前采用此机制的字段有:

  • weixin_session_key

加密算法:AES-256-GCM

加密密钥:游戏的 Secret Key

使用服务端 SDK

使用 Go 或 Node.js 服务端 SDK 时,SDK 在验证 Identity Token 的同时会自动完成解密,游戏服务端可直接读取明文字段值,无需手动处理解密逻辑。

不使用服务端 SDK

如果游戏服务端未使用 Combo 服务端 SDK,需在验证 JWT 签名后,对加密字段手动进行 AES-256-GCM 解密,密钥为游戏的 Secret Key。密文格式及解密实现可参考服务端 SDK 的源码:combo-sdk-go / combo-sdk-node

验证逻辑

游戏服务端应当对 Identity Token 进行验证,验证不通过说明游戏客户端身份不可信,应当拒绝客户端进入游戏。

验证内容如下:

  1. Token 能够被解析成一个结构正确的 JWT
  2. Token 的 iss 和当前 API 服务端点匹配,举例 https://api.seayoo.com
  3. Token 的 scopeauth
  4. Token 的 aud 与世游分配的 Game ID 匹配
  5. Token 的 exp 字段指示的过期时间,服务端的当前时间尚未达到
  6. Token 的 HS256 签名与服务端使用 Secret Key 计算出的签名匹配

如果上述验证均通过,说明 Identity Token 是可信的,游戏服务端可以使用 Claims 用于登录环节的业务逻辑处理。

有效期

Identity Token 的有效期目前为 168 小时(7 天)。

开源库

JWT 的开源库非常多,游戏服务端可在 https://jwt.io/libraries 中选择适合自己的。