Skip to content

第三方App嵌入H5技术方案

嵌入对接文档

基于appid, appkey 的app嵌入模型接入方案。

假定当前在接入的公司叫 A公司。

完成流程:基于服务端预授权的静默登录方案

本方案采用 后端预换取授权码 (Auth Code) 的模式。此模式安全性最高,因为 appkey 仅存储在 A公司 Server 端,完全不暴露给客户端(App 或 H5)。

1. 核心角色定义

  • A公司 App (客户端): 用户已登录,持有 A公司的 Session。
  • A公司 Server: 持有我方颁发的 appidappkey,作为可信的身份提供方。
  • 我方 H5: 嵌入在 App Webview 中的页面。
  • 我方 Server: 资源拥有者,维护 users 表,负责验证身份并发放我方 Session。

2. 准备工作

  • 我方为 A公司分配唯一的 appid (应用标识) 和 appkey (签名秘钥)。
  • appkey 必须严格保存在 A公司 Server 端,严禁 下发到 App 客户端。

3. 交互时序图

sequenceDiagram
    participant User as 用户X
    participant App as A公司 App
    participant AServer as A公司 Server
    participant MyServer as 我方 Server
    participant H5 as 我方 H5页面

    Note right of User: 用户在 A公司 App 内已登录

    User->>App: 点击进入“我方H5业务”

    Note right of App: 步骤1:请求免登入口
    App->>AServer: 1. 请求获取 H5 跳转链接 (携带当前 UserID)

    Note right of AServer: 步骤2:服务端身份握手 (核心鉴权)
    AServer->>AServer: 使用 appkey 生成签名 (Sign)
    AServer->>MyServer: 2. 调用“获取授权码接口”<br/>(参数: appid, sign, A公司UserUniqueID, 用户信息)

    Note right of MyServer: 验证签名合法性<br/>记录 A公司UserUniqueID<br/>生成一次性 AuthCode
    MyServer-->>AServer: 3. 返回 AuthCode (有效期如 1分钟)

    Note over AServer, App: 步骤3:返回带票据的 URL
    AServer-->>App: 4. 返回完整 URL: https://my-h5.com?authCode=AuthCode

    Note over of App, H5: 步骤4:打开页面
    App->>H5: 5. 打开 WebView 加载 URL

    Note over H5, MyServer: 步骤5:兑换 Session (静默登录)
    H5->>H5: 解析 URL 中的 code 参数
    H5->>MyServer: 6. 提交 code 进行登录 (POST /lemon/ss_oauth/verify_code)

    Note right of MyServer: 验证 AuthCode 有效性<br/>取出关联的 A公司UserUniqueID<br/>查找或创建我方 User<br/>生成我方 AccessToken
    MyServer-->>H5: 7. 返回 AccessToken & UserInfo

    H5->>H5: 存储 AccessToken (Localstorage/Cookie)
    H5-->>User: 8. 展示业务页面 (已登录状态)

4. 详细步骤解析

第一阶段:获取授权码 (Server-to-Server)

这一步发生在用户点击入口的瞬间,A公司 App 移动端 不需要知道 appid/appkey,也能通过请求自己的后端来完成鉴权。

  1. App 发起请求: App 告知 A公司 Server:“当前用户 (UserA) 需访问 H5”。
  2. A公司 Server 签名请求:

    • A公司 Server 准备参数:appid, timestamp, nonce (随机串), third_party_uid (简写为 tp_uid) (A公司用户的唯一ID), user_profile (可选,昵称头像等)。
    • 调用我方接口:POST https://api.gouboyun.tv/lemon/ss_oauth/generate_code

    • 接口算法在下文详细描述。

  3. 我方 Server 验证与发码:

    • 我方收到请求,根据 appid 找到对应的 appkey
    • 重复计算签名,对比 sign 是否一致。
    • 验证通过后,将 third_party_uid 与一个临时的 AuthCode (如 UUID) 绑定,放入临时缓存,设置短过期时间 (如 60秒)。
    • 返回 AuthCode 给 A公司 Server。

第二阶段:页面跳转与登录 (Client-Side)

  1. 拼接 URL: A公司 Server 收到 AuthCode 后,拼接成 H5 访问地址:https://h5.myserver.com/index?authCode={AuthCode},返回给 App。
  2. App 打开 H5: App 使用 WebView 加载该 URL。
  3. H5 兑换 Token:
    • H5 页面初始化时,检查 URL 是否有 code 参数。
    • 如果有,立即调用我方接口:POST https://api.myserver.com/auth/login_by_code,Body: { authCode: "..." }
  4. 我方 Server 完成登录:
    • 验证 code 是否存在且未过期。
    • 取出之前绑定的 third_party_uiduser_profile
    • 账号映射 (关键): 查询我方 users 表,是否已存在绑定了该 third_party_uid 的用户。
      • 若存在: 直接生成我方 AccessToken
      • 若不存在: 自动注册新用户 (使用 user_profile 填充资料),记录绑定关系,然后生成 AccessToken
    • 销毁 code (防止重放)。
    • 返回 AccessToken 给 H5。

5. 关键接口定义 (参考)

接口 A: 获取授权码 (Server-to-Server)

  • 调用方: A公司 Server -> 我方 Server
  • 路径: https://api.gouboyun.tv/lemon/ss_oauth/generate_code
  • Method: POST
  • 参数:
    • appid: String
    • ts: 时间戳 数字类型 Long,单位秒
    • nonce: String (随机字符串) 长度小于 16 ,urlsafe_encoded
    • tp_uid: String (third_party_uid的简称) (A公司用户的唯一标识)
    • tp_data: JSON String (用户基础信息:昵称、头像,用于自动注册)
    • sign: String (签名)
  • 返回: { "code": "xxxx-xxxx", "expires_at": 191920203090 }
curl -H "Content-Type: application/json" https://api.gouboyun.tv/lemon/ss_oauth/generate_code -d \
'{
  "appid": "aabbccdd",
  "ts": 191920203030,
  "nonce": "29f023be",
  "tp_uid": "11223344",
  "sign": "eeeae131d48a98e29efd75ae1d17a193",
  "tp_data": {
    "nickname": "abc",
    "phone": "13800000000", // 可选
    "avatar": "https://some.url.pic.jpg"
  }
}'

sign签名算法

除去 sign 字段的 如下几个参数,根据 字符串排序,

  • 生成签名的时候,将颁发的 appkey 加入到传递的参数中,参与加密,为简化对接过程,签名计算仅需要如下参数:
    • appid
    • appkey
    • nonce
    • tp_uid
    • ts
  • 传递的参数(包含appkey)按照参数名升序排序,然后,以&形式连接(类似格式为a=xxx&b=xxx&c=xxx...),生成小写的md5串
  • 生成sign后,sign和其他参数一起传递,对于中文在传递的过程中,需要进行urlencode,加密的时候不进行urlencode(如是以POST方法json格式传参不需要urlencode)
  • appkey 不需要放入最终传递的数据 json 中!!!!

签名对比值

实际发送值

{
    "appid": "aabbccdd",
    "appkey": "aabbddcc",
    "nonce": "29f023be",
    "tp_uid": "11223344",
    "ts": 191920203030
}

签名结果

发送出的请求

接口 B: 授权码登录 (H5 -> Server)

  • 调用方: 我方 H5 -> 我方 Server
  • 路径: /api/client/auth/login
  • 参数:
    • code: String
  • 返回: { "token": "eyJhbG...", "user": { ... } }

6. 安全性考量

  1. AppKey 不泄露: 由于签名过程完全在 A公司后端进行,App 端和网络传输中均不包含 AppKey,极大降低了泄露风险。
  2. AuthCode 一次性: 授权码仅使用一次即销毁,且有效期极短(建议 30-60秒),防止被截获后重放。
  3. 签名防篡改: 包含 timestampnonce,防止请求被恶意修改或重放攻击。

简化版本的对接

此对接模型,仅作为快速对接使用,不建议放到生产环境中。

appid, appkey 直接放入到 https://api.gouboyun.tv/lemon/ss_oauth/generate_code 请求中。

curl https://api.gouboyun.tv/lemon/ss_oauth/generate_code -d '{
    "appid": "aabbccdd",
    "appkey": "aabbddcc",
    "ts": 191920203030,
    "nonce": "29f023be",
    "tp_uid": "11223344",
    "tp_data": {
        "nickname": "abc",
        "avatar": "https://some.url.pic.jpg"
    },
    "sign": "eeeae131d48a98e29efd75ae1d17a193"
}'

页面渲染过程

当app对接完成token兑换之后,h5页面会自动获取到最新的 token,并使用此token完成后续页面流程交互。

token过期失效的场景

因app长时间切换到后台,再次切回,会出现token过期的场景,此处需要根据公司设定的 url_scheme ,比如

wechat://re-initial-oauth 地址页面,

自动跳转到 app 的重新授权页面,此处需要A公司的APP开发人员,设置并处理 wechat://re-initial-oauth 相关呼叫。