如何在本地开发环境调试微信 JS-SDK

5,191

以下篇幅将会描述不同前提下对应的调试策略,当然也有可能不是最优解,望斧正 →_→


前言

何谓「安全域名限制」?

以微信 JS-SDK 的使用为例,每个公众号被限制最多可设置三个安全域名,且必须能被腾讯服务器所验证(这意味着域名必须绑定在一台可被外网访问的服务器上);然后只允许在这几个域名下才能使用 JS-SDK,这就是安全域名限制。

这种策略应用还相对比较广泛,大概可以等同于三方提供的 API 需要让你配置 IP 白名单。

下图大致描述了 JS-SDK 域名验证的过程,其中

  • WeixinWebview 为微信客户端内的浏览器;
  • BuServer 为我们自己的业务服务器;
  • WeixinClient 为微信 App;

以下流程的前提是 BuServer 已经具有签名所需的 access_token、jsapi_ticket 等信息

建立在这个基础上,我们想绕过这个安全策略,大概有两种方案:

  1. 常规操作,将一个固定的域名作为测试专用,填入安全域名列表里,占掉一个坑「这边当然可以专门申请个测试账号」,然后通过解决内网穿透「ngrok、花生壳之流」便可以实现;
  2. 非常规操作,通过修改 DNS 解析实现,将特定资源「或所有资源」的请求重写到本地开发服务上;

常规操作

  1. 通过 ngrok / 花生壳内网穿透,一般工具都会给你临时域名,固定的域名需要付费;
  2. 将域名填入微信安全域名列表中,并将验证文件放入 Web 容器里进行验证;
  3. 完成了上面两步,你就可以愉快的调试啦;

非常规操作

从上文流程图中可以看到 JS-SDK config 信息是通过当前请求页面的 URL + jsapi_ticket + appid + ...... 按照一定规则进行签名得到的,然后将这个 config 传入微信客户端进行校验是否合法。

整个验证过程中,我们能 hack 的是将请求的资源转发到开发服务器上,从而能调试本地的代码。

围绕「将请求的资源转发到开发服务器」,可以想到下面两个方案:

  1. 将请求拦截,转发到开发服务器上;
  2. 利用 DNS 解析,将安全域名解析到开发服务器上「俗称 DNS 劫持」;

方案一 抓包工具转发

本文不过多赘述,可以通过 Charles,Fiddler 这些抓包工具对指定的请求进行转发,从而达到目的。

方案二 DNS 劫持

可以先看下图 DNS 解析过程:

要实现 DNS 劫持,最现实的是通过修改开发机的 hosts 文件将安全域名解析到本地开发环境 ip 上。

HTTP 请求的劫持

如果你的网站提供的是 HTTP 服务,只需要修改开发机中 hosts 文件,将安全域名指向开发服务。看到这里就差不多了,可以自己去实践了。

HTTPs 请求的劫持

但是目前大多数商业网站都 HTTPs 化了,因此并不能简单的通过 DNS 劫持,把请求转发到本地的 HTTP 服务上。

如果你的安全域名是一个 HTTPs 服务,那可能有点麻烦。首先我们来看看一次像 HTTPs Server 请求的时间线:

如上图可以看到当像 HTTPs Server 发起一个 HTTP 请求时,将会被永久重定向成 HTTPs 请求,然后响应头中会有一个特殊的头「strict-transport-security」,这个头是告知浏览器该域下的所有请求都将使用 HTTPs 访问。因此接下来如果再发起一个 HTTP 的请求,浏览器将会内部重定向至 HTTPs 请求。

上文对 HTTPs 请求的描述是为了引出当你安全域名对应的是 HTTPs 服务时,会遇到如下问题:

  1. 将安全域名 DNS 劫持到本地 HTTP 服务后,在浏览器中访问仍旧是 HTTPs 请求;
  2. 基于 1 ,我们想到清浏览器缓存,但是微信开发者工具无法进入 chrome://net-internals/#hsts 清理;

Q1:以 Chrome 为例,进入 chrome://net-internals/#hsts,清除安全策略。

Q2:在微信开发者工具中,我们并不能进入这个页面,也就意味着如果安全域名在该工具中有安全策略缓存,则你无法将它劫持到一个 HTTP 服务,「这边如果有什么方案,可以告诉我」。

为了解决这个问题,我们只能引入终极解决方案,通过一个 HTTPs Server 对请求做一个转发,具体流程如下:

ProxyServer 做了两件事:

  • 配置了安全域名对应的 SSL 证书,支持了 HTTPs 请求;
  • 将安全域名下的请求转发到对应开发服务上;

如果这个 ProxyServer 是作为一个通用服务存在,则需要考虑如何代理到不同开发者启动的开发服务上。

最开始我们计划通过 querystring 来制定需要代理到的开发服务的地址,实际情况是一个页面中所有依赖该域下的请求都得转发,因此 querystring 的方法不能很方便的实现这个功能。

因此最后通过 cookie 来实现这个需求,通过 cookie 指定开发服务器地址,这样该域下的所有资源请求都会带上该 cookie,就能实现自定义转发到上游服务了。

最终使用该方案调试微信 JS-SDK 需要做如下两步:

  1. 更改 hosts 文件,将安全域名劫持到代理服务上;
  2. 设置 cookie ,指定需要代理到的上游服务;

这样一来,该代理服务就可以作为通用服务所存在,任何开发者有绕过安全域名限制做一些调试,都可以使用这个服务。

FAQ

Q1. 常规操作的优劣?

Pros:

  1. 步骤比较少,容易搞,适合偶尔需要调试;

Cons:

  1. 固定域名要钱💰啊,不固定域名又要经常改,但是一个月又只有三次修改机会啊;
  2. 微信安全域名池子没坑怎么办啊。。。「申请个测试账号也不是不行,但是这样后端 access_token、jsapi_ticket 相关接口都得修改」;
  3. 不够通用,其他三方登录有安全域名设置,并不能复用;

针对 Cons 1 有更优解,可以结合 DNS 劫持,在微信后台绑定安全域名时使用花生壳验证域名,然后其实不需要再使用花生壳了,可以修改 hosts,将验证的域名劫持到本地开发服务,这样既不用操心 HTTPs 也不用付费要固定域名。有兴趣的小伙伴可以尝试尝试。

Q2. 非常规操作又有什么优势?

Pros:

  1. 不需要额外的域名,意味着不用改安全域名配置,服务端代码也不用动;
  2. 通用,有类似安全域名策略的三方服务通杀;
  3. 对于普通开发者来说日常开发调试成本很低,也不需要什么特殊权限;

Cons:

  1. 搭建流程相对复杂点,需要服务器资源;

Q3. 真机微信里怎么调试啊?

祭出 Charles,Mac 用 Charles,Win 用 fiddler 开代理,如果是 HTTPs 则还需要配置证书信任,这个步骤本文不再赘述,可自行 Google。

Q4. 怎么申请测试账号?

mp.weixin.qq.com/debug/cgi-b…

小结

解决方案上,常规方案的额外成本较低,但是在多个开发者协同开发公众号的情况下,综合成本较高;非常规方案比较适合多个开发者需要调试微信 JS-SDK,一劳永逸。

在整个过程中,其实可以了解到 DNS 劫持的概念、实际应用,以及对 HTTPs 的进一步了解。

最后,如果喜欢可以关注公众号「茶杯盖」