H5 拉起微信小程序流程解析

前端开发 Jun 7, 2022

非个人主体并且已认证的(微信认证)小程序,使用云开发静态网站托管的网页,可以免鉴权跳转任意合法合规的小程序。即可以在微信内部浏览器的 H5 跳转小程序,也可以在微信外部浏览器或其他部分 App (如企业微信、QQ 等)跳转微信小程序(参见微信文档:静态网站 H5 跳小程序)。

H5 拉起微信小程序流程解析

微信浏览器内

由于 H5 拉起小程序是在微信浏览器封闭的系统内,微信有着较高的定制和管控能力。

在实现方案上,微信未提供直接拉起小程序的 API,而是提供了:供用户点击跳转小程序的开放标签 (从 2020 年中开始提供)。使用方法如下:

<wx-open-launch-weapp
  id="launch-btn"
  username="gh_xxxxxxxx"
  path="pages/home/index?user=123&action=abc"
>
  <template>
    <style>.btn { padding: 12px }</style>
    <button class="btn">打开小程序</button>
  </template>
</wx-open-launch-weapp>

在需要用到跳转能力的页面中,首先引入微信 js-sdk,并在期望用户点击跳转的地方引入该开放标签即可。其中,gh_xxxxxxxx 为目标跳转的小程序原始 ID,线上小程序均可在”更多资料“一栏查看。

另外,在使用该标签的页面中,还需首先调用 wx.config 进行鉴权,否则该标签不展示。下面详细分析其流程。

获取 signature

首先通过服务端获取到了主体使用权的签名 signature。

(注意这里获取 signature 中使用的 appId 与开放标签中使用的 appId 不是同一个:这里用的是主体服务号的 appId,开放标签中使用的是跳转的目标小程序的 appId)。

签名校验

接着将 signature 作为参数供 wx.config 调用。 方法传入参数为:

wx.config({
  // debug: true, // 调试时可开启
  appId: "必填,公众号的唯一标识", // <!-- replace -->
  timestamp: 0, // 必填,填任意数字即可
  nonceStr: "nonceStr", // 必填,填任意非空字符串即可
  signature: signature, // 必填,填服务端获取的签名
  jsApiList: ["chooseImage"], // 必填,随意一个接口即可
  openTagList: ["wx-open-launch-weapp"], // 填入打开小程序的开放标签名
});

调用该方法后,经过多处调用,最终进入下面的逻辑:

o.WeixinJSBridge
  ? WeixinJSBridge.invoke(n, x(e), function (e) {
      A(n, e, i);
    })
  : B(n, i);

其中 X(e) 为拼接 signature 字段等。看 invoke 方法,其中 n 为 'preVerifyJSAPI' 方法。

invoke 方法在 js-sdk 中找不到定义,想到微信 app 可以加载执行 JS 代码,于是解压了微信 apk,找到 assets/wxjs.js,在里面找到了定义:

WeixinJSBridge.invoke = function _call(func, params, callback) {
  // 省略
  var msgObj = {
    func: func,
    params: params,
  };
  msgObj[_MESSAGE_TYPE] = "call";
  msgObj[_CALLBACK_ID] = callbackID;

  try {
    _sendMessage(JSON.stringify(msgObj));
  } catch (e) {
    __initLog(__EL, "_call error", e);
  }
};

核心为 _sendMessage 方法:

function _sendMessage(msg) {
  // 省略 msg -> msgArrayString
  __wx._sendMessage(msgArrayString);
}

可以看到最终是将传入的参数调用 __wx._sendMessage 方法,其中 __wx 对象从 window.__wx 上取得,大概率是 Native 上定义的对象,并注入到 JS 环境中。

(注:JS 调用 Native 方法可参见:H5 与 Native 的交互方案,看 wxjs.js 中,暂时用的 addJavascriptInterface 方案,但 iframe 发起 URL Schema 的方式也写了做备份。)

鉴权结果回调

鉴权结果要通知 JS 侧,需要 Native 调用 H5 的方法,来看其中的 _handleMessageFromWeixin 方法:

function _handleMessageFromWeixin(message) {
  // 省略
  switch (msgWrap[_MESSAGE_TYPE]) {
    case "callback":
      {
        if (
          typeof msgWrap[_CALLBACK_ID] === "string" &&
          typeof _callback_map[msgWrap[_CALLBACK_ID]] === "function"
        ) {
          var ret = _callback_map[msgWrap[_CALLBACK_ID]](msgWrap["__params"]);
          delete _callback_map[msgWrap[_CALLBACK_ID]]; // can only call once
          return JSON.stringify(ret);
        }
        return JSON.stringify({
          __err_code: "cb404",
        });
      }
      break;
    case "event":
    // 省略
  }
}

可以看到在这里执行 callback() 回调,并传入 Native 处理结果,wx.config 的回调方法为 A(n, e, i),没有太多重要的操作。

在实际页面中发现,如果是鉴权不通过,那么开放标签不会显示,所以也研究了下微信的开放标签

开放标签像是 webComponent,但很可惜没有在解压的微信 apk 中找到对应的定义,应该是从浏览器底层支持了。

小结

在微信内部浏览器中,通过鉴权的网站使用 <wx-open-launch-weapp> 标签即可拉起小程序。而通过上面流程的梳理发现,只要服务号主体配置了安全域名,那么理论上这个页面,可以在微信内置浏览器内跳转任意小程序。网上有相关尝试证实确实如此。暂时没发现有做其它管控。

其它浏览器

如前所述,<wx-open-launch-weapp> 是微信内提供的标签,在其它浏览器中无法使用,但官方也提供了其它浏览器拉起小程序的方法,即使用 URL Scheme 的方式。

外部网页使用: location.href = "weixin://dl/business/?t= *TICKET*" 的方式即可拉起小程序。

其中 TICKET 的获取方式与上文中 signature 类似,是在三方服务端通过 token 去请求小程序服务端获得,需要的参数是 appId 和 AppSecret ,在这里使用的 appId 和 AppSecret 即为将要拉起的目标小程序。可以选择到期失效,也可以选择永久有效。

所以三方媒体页面要拉起某个小程序,需要首先获得该小程序的许可,拿到其 TICKET 才行。但 TICKET 可以选择永久有效,所以从原理上说,如果是永久有效,存在被另外主体获取并盗用的可能。

获取 TICKET 的过程并没有用到安全域名配置的信息,所以任意的网站只要能拿到 TICKET 均可以调起小程序。(另外,URL Scheme 的方式也可用于从其它 app 中调起打开小程序。)

另外,由于是使用 location.href = "weixin://dl/business/?t= *TICKET*" 的方式调起小程序,所以理论上是网页可以自动拉起,实测也确实如此。

小结

外部浏览器通过 URL Scheme 的方式调起小程序,无论什么网站,只要能生成对应小程序的 TICKET 即可拉起该小程序,几乎没有管控。

总结

2020 年中,微信开放的拉起小程序的能力,在微信浏览器内做的管控为:需 为媒体网站配置安全域名 + 用户点击 方可跳转小程序;外部浏览器通过 URL Scheme 的方式做的管控为:需事先获取目标小程序的 TICKET,但整体来说该方式调起小程序则相对灵活,且可以由网页自动拉起小程序(需要说明的是,个人小程序,不具有获取 TICKET 或 signature 的权限)。

原文首发于:H5 拉起微信小程序流程解析 | 宜想悠然亭

您可能感兴趣的文章

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.