微信JS-SDK公众平台网页开发大坑硬填

4,088 阅读3分钟

题目

本帖针对微信JSSDK网页开发中碰到的坑,讲述填坑过程。

官网: developers.weixin.qq.com/doc/offiacc…

流程

步骤一:获取公众号的access_token准备工作

目的:正如所有公开平台,都需要注册并获取appID和appSecret,以便你开发的“app”可以访问该公众号的资源(公众号不一定是你所有,可以是的客户的,只要提供appID和appSecret即可)

方法:获取公众号的appID和appSecret,并设置你的服务器的ip地址(如果本地开发,则用本机IP:百度搜索ip即可

image.png

步骤二:设置你的网页的domain

目的:白名单你的域名,这样你的网页比如http(s)://domain.com/page.index 就可以调用JS-SDK来获取微信平台/app暴露给你的功能,比如拍照,录音,支付等原生的H5没有的功能,可以把JSSDK看成是一个桥接器。

为什么要这样?你可能有这样的疑问。确实,类似的开发平台有时候设置很麻烦,容易让人摸不着头脑。为了快速理清思路,我一般会这么想:如果我是平台方,我会怎么做?所以,如果你是微信方,你会不会想:虽然我有接口让开发者动态提供url并给他返回签名,见签名文档,但是这个url我没有做任何限制,会不会有安全隐患,就算没有安全隐患也要管控吧?所以,要提供白名单功能。

image.png

方法:如图,输入根域名和www次域名(不一定要输入,这个不一定,没有测试)

条件:

  1. 域名需要icp
  2. 用txt文件验证域名所有权

我的操作,方法1

  1. 在linux部署nginx(腾讯服务器,因为好像icp备案完成后也需要指向一个服务器)
  2. 设置nginx(大概如下)

server {
    root /var/www/domain.com;
    index index.html  index.htm;
   server_name  www.domain.com domain.com;
    location / {
        try_files $uri $uri/ /index.html?/$request_uri;
    }
}

  1. 部署certbot添加https

  2. 把txt文件放入 /var/www/domain.com

  3. 公众号验证成功

另外一个方法2(没有测试):

  1. 免费获取阿里云或者腾讯云的证书(免费的不是通配符,比如可以是www.domain.com)

  2. 开通腾讯云COS或者阿里的OSS,把文件夹绑定到域名上面(通过CDN)

  3. 把txt文件放到OSS或者COS上面

  4. 公众号认证

结论:

  1. 公众号txt认证,似乎认证不需要https协议,http即可(除非你用的是方法2,因为CDN需要https)

  2. 白名单后,测试网页域名似乎不需要https(测试http可以)

  3. 白名单根域名后(domain.com)后,测试网页似乎可以是其他次域名,比如ngrok.domain.com

第2,3结论,请继续看。

步骤三:服务器获取signature

目的:服务器(步骤一,白名单的IP)从微信拿到签名

import cache from 'memory-cache';
import sha1 from 'sha1';
import config from '../config';
import { axiosGet } from './util';


export default function (url: string) {

    return new Promise((resolve, reject) => {
        const noncestr = config.wechat.noncestr;
        const timestamp = Math.floor(Date.now() / 1000); //精确到秒

        if (cache.get('ticket')) {
            console.log('get wechat ticket from memory cache...success');
            const jsapi_ticket = cache.get('ticket');
            return resolve({

                openTagList: "wx-open-launch-weapp",
                appId: config.wechat.appid,
                nonceStr: noncestr,
                timestamp: timestamp,
                url: url,
                jsapi_ticket: jsapi_ticket,
                signature: sha1('jsapi_ticket=' + jsapi_ticket + '&noncestr=' + noncestr + '&timestamp=' + timestamp + '&url=' + url)
            });
        }

        console.log('ticket expired, refetching token and ticket...');

        axiosGet(config.wechat.accessTokenUrl, {
            grant_type: config.wechat.grant_type,
            appid: config.wechat.appid,
            secret: config.wechat.secret
        }).then(res => {

            const accessToken = res.data.access_token;
            axiosGet(config.wechat.ticketUrl, {
                access_token: accessToken,
                type: 'jsapi',
            }).then(res => {
                const ticket = res.data.ticket;

                cache.put('ticket', ticket, config.wechat.cache_duration);

                resolve({
                    appId: config.wechat.appid,
                    openTagList: "wx-open-launch-weapp",

                    nonceStr: noncestr,
                    timestamp: timestamp,
                    url: url,
                    jsapi_ticket: ticket,
                    signature: sha1('jsapi_ticket=' + ticket + '&noncestr=' + noncestr + '&timestamp=' + timestamp + '&url=' + url)
                });
            }).catch(err => {
                reject(err);
            });
        });

    });
}

//util
export const axiosGet = async <T>(baseUrl: string, params: T) : Promise <any> => {
  try { 
    return await axios.get(baseUrl, {
        params
    });
  }catch (e) {
    console.log(e);
  }
};

结论:

  1. 先获取access_token (微信设置两小时过期,见官网)

  2. 然后换取js_ticket (微信设置两小时过期,见官网)

  3. 自己设置缓存,防止反复请求微信造成拒绝

步骤四:本地开发网页H5并调取JSSDK

目的:开发前端

方法: 用微信开发者工具IDE开发(用浏览器调用JSSDK没有反应,因为不是微信模拟环境)

image.png

问题来啦:如果你用本地开发localhost在IDE中会无法访问接口,错误如下:

  • localhost,在微信开发者工具中调试时报以下错误:
  • xxxx:fail, the permission value is offline verifying

蹩脚的英语的意思是:网页为离线网页,即没有在服务器上。

步骤五:填补大坑

目的:把认证的域名domain.com映射到本地localhost,解决如上问题。问题的根源是,似乎IDE在夹在JSSDK的时候,会把当前的url传递到微信后端,并核对是否是认证的域名,如果不是则报错。

方法:首先讲,这个开发非常不友好。

  1. 开发不可能把网页放到真实服务器上开发,几乎是自杀式开发

  2. 微信为啥不能白名单localhost,给程序员提供一个友好的开发环境

  3. 微信为啥不能提供一个沙箱测试appID来放开localhost限制

既然,没有这样的环境,只能想办法自己解决。

基本思路:把认证的域名映射到本地localhost,然后在IDE输入域名,欺骗微信IDE,即:domain.com -> localhost

方法1(macos):

  1. 编辑hosts文件 $ sudo nano /etc/hosts

  2. 映射domain到localhost

localhost www.domain.com

  1. 测试

ping www.domain.com

  1. 用www.domain.com开发,结果发现失败。 因为代码放在localhost 3000端口上。默认的localhost在80端口。

  2. 需要把代码加载到80端口,比如用reactjs的话是

sudo PORT=80 react-scripts start

注意:80端口小于1024,所以需要sudo权限

如何把domain映射到任意的localhost的端口呢?

方法1:

  1. 购买ngrok的服务ngrok.com,大概是25美金pro版

  2. 把domain域名绑定到ngrok, 设置CNAME进行绑定比如:ngrok.domain.com(不影响你现有的www.domain.com访问互联网,CNAME设置也只是影响ngrok次域名)

  3. 下载ngrok,并运行

$ ./ngrok http --hostname=ngrok.domain.com 3000

  1. 成功

⚠️注意:ngrok的设置方法和config文件如下

⚠️注意:在wechat的IDE上面如果你输入 ngrok.domain.com的话,访问本地服务器可能会有跨域错误,所以要用https

nano .ngrok2/ngrok.yml

image.png

参数ngrok.com/docs/ngrok-…

方法2:(没有测试)

  1. 本地搭建nginx服务器

  2. 把domain映射到loalhost

  3. 然后通过nginx的vhosts开放不同的端口