美文网首页
nodejs后端写一个生成微信signature的接口

nodejs后端写一个生成微信signature的接口

作者: 时间染上空白 | 来源:发表于2018-12-19 17:38 被阅读0次

    我们平常在使用微信内置浏览器访问第三方web网页的时候,通过右上角的三个点按钮可以把这个网页分享出去。一般分享出去的是网址链接的展现形式,但我们希望可以看出缩略图,标题,摘要,然后样式良好,这样给用户的体验也很好。

    所幸,微信也是支持这种体验良好的分享方式,不过我们需要通过调用微信的JS-SDK来实现自定义分享效果。在这里,我主要讲的是:如何在nodejs后台环境下写一个生成微信签名的接口(nodejs+Koa 后台环境)。原谅小编只是苦逼的前端一枚,只会一点三脚猫nodejs功夫。>.<

    • 前置工作

    这里默认是已经做好诸如配置公众号、前端引入使用jdk等前置工作。如果还没有做或者不会的童鞋,请移步以下教程(这个教程非常的全面):

    手把手带你使用JS-SDK自定义微信分享效果

    • 问题与实现思路

    首先,通过官方文档我们知道要生成signature ,我们必须要获取 accessToken 和jsapi_ticket 这两个从微信官方请求回来的东东,才能生成签名。但是问题并没有看上去的这么简单容易。感兴趣的可以查看下官方文档:

    https://mp.weixin.qq.com/wiki?action=doc&id=mp1421141115&t=0.15697429783636763#buzhou3

    所以必须要解决以下两个问题:

    1. 必须AppId和AppSecret请求accessToken,然后通过accessToken获取jsapi_ticket
    2. accessToke 每日限请求2000次,jsapi_ticket 每日限请求100000次,有效期都是7200秒(2小时)。

    解决方法:

    1. 必须采用串行方法,首先获取到accessToken,再把accessToken作为参数来获取
      jsapi_ticket。所以这里使用了Promise来实现串行方法。

    2. 其他方法大体上采用全局方法来缓存signature 的值,前台每次请求生成签名接口时校验有没有过期。当前这里采用的是nodejs的fs模块写入一个本地的json文件,用来存储jsapi_ticket和上次请求的时间。当每次接口被请求时,读取这个json文件,校验过期。

    具体实现代码实现:

    const router = require("koa-router")();
    const fs = require("fs");
    const path = require("path");
    const moment = require("moment");
    const request = require("request");
    var crypto = require("crypto");
    
    const filePath = path.join(__dirname, "/data.json");
    router.post("/wxconfig", async (ctx, next) => {
      const req = ctx.request.body;
      console.log(req);
      let nowUrl = req.url;
    
      // 定义两个函数返回Promise对象,用来组成串行,并最终获取到jsapi_ticket后最终处理成签名。
    
      // 获取accessToken
      const getToken = function() {
        let p1 = new Promise((reslove, reject) => {
          request(
            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=这里是appid&secret=这里是appsecret",
            function(error, response, body) {
              if (!error && response.statusCode == 200) {
                console.log(body);  // 注意返回的数据是一个纯字符串,要格式化处理
                let token = JSON.parse(body).access_token;
                if (token !== "") {
                  reslove(getJsapi(token));
                }
              }
            }
          );
        });
        return p1;
      };
    
      // 获取jsapi_ticket
      const getJsapi = function(token) {
        let p2 = new Promise((reslove, reject) => {
          request(
            "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" +
              token +
              "&type=jsapi",
            function(error, response, body) {
              if (!error && response.statusCode == 200) {
                console.log(body); // 注意返回的数据是一个纯字符串,要格式化处理
                // 存储当前 ticket
                const ticketData = {
                  jsapi_ticket: "",
                  time: moment().format("YYYY/MM/DD HH:mm:ss")
                };
                if (JSON.parse(body).errcode === 0) {
                  // 如果成功获取到
                  ticketData.jsapi_ticket = JSON.parse(body).ticket;
                  /* 这里是将在这个同级目录下创建一个json文件用来存储jsapi_ticket,
    和请求时间,用于下次接口被调用的过期校验。*/
                  fs.writeFile(filePath, JSON.stringify(ticketData), function(err) {
                    if (err) console.error(err);
                    console.log("写入ticketData的json文件成功!");
                  });
                  reslove(JSON.parse(body).ticket);
                } else {
                  fs.writeFile(filePath, JSON.stringify(ticketData), function(err) {
                    if (err) console.error(err);
                    console.log("写入ticketData的json文件失败!");
                  });
                }
              }
            }
          );
        });
    
        return p2
          .then(result => {
            console.log(result);
            // 在这里返回签名生成函数的结果给前台
            let sendData = getSignature(nowUrl, result);
            ctx.status = 200;
            ctx.body = sendData;
          })
          .catch(err => {
            console.log(err);
          });
      };
    
    /* 这里是先判断存储json文件是否存在,若不存在或者文件存在但已过期,
     就调用上方的串行函数,直接返回生成的签名给前台。若文件存在没过期,
     直接使用json文件中的jsapi_ticket生成签名返回给前台使用。*/
    
      if (fs.existsSync(filePath)) {
        console.log("文件路径存在");
        // 先读取
        const jsapiData = JSON.parse(fs.readFileSync(filePath));
        console.log(jsapiData);
        // 先判断时间是否过期,若不过期传key,过期不传key
        let t1 = jsapiData.time; // 数据,必须是2018/12-/01 12:09:04这种格式,否则Date对象无法转换
        let dateBegin = new Date(t1); // 转化为Date对象的形式
        let dateEnd = new Date(); //当前时间数据
        let dateDiff = dateEnd.getTime() - dateBegin.getTime(); //时间差的毫秒数
        // console.log(Math.floor(dateDiff / 1000))
        if (Math.floor(dateDiff / 1000) > 7198) {
          // 缓存时间超过有效期(过期)
          sendData = await getToken();
        } else {
          // 不过期,调用签名生成函数生成结果直接ctx返回给前台
          let signaData = await getSignature(nowUrl, jsapiData.jsapi_ticket);
          ctx.status = 200;
          ctx.body = signaData;
        }
      } else {
        console.log("文件路径不存在");
        sendData = await getToken();
      }
    });
    
    // 生成签名函数
    const getSignature = function(nowUrl, key) {
      let noncestr = Math.random()
        .toString(36)
        .substr(2); // 随机字符串
      let timestamp = moment().unix(); // 获取时间戳,数值类型
      let jsapi_ticket = `jsapi_ticket=${key}&noncestr=${noncestr}&timestamp=${timestamp}&url=${nowUrl}`;
      // console.log(jsapi_ticket)
      jsapi_ticket = getSha1(jsapi_ticket);
      return {
        noncestr: noncestr,
        timestamp: timestamp,
        signature: jsapi_ticket
      };
    };
    
    /**
     * @sha1加密模块 (加密固定,不可逆)
     * @param str string 要加密的字符串
     * @retrun string 加密后的字符串
     * */
    const getSha1 = function(str) {
      var sha1 = crypto.createHash("sha1"); //定义加密方式:md5不可逆,此处的md5可以换成任意hash加密的方法名称;
      sha1.update(str);
      var res = sha1.digest("hex"); //加密后的值d
      return res;
    };
    
    module.exports = router;
    
    
    • 注意事项
    1. 微信的签名必须使用sha1的加密方式,而nodejs提供有通用的加密和哈希算法模块 crypto,直接引用即可。
    2. moment和request分别是第三方的 生成时间 和 ajax请求使用,所以需要使用npm install 一下,具体用法自行百度。
    3. 最好对照微信官方的校验工具,检查下处理的签名是否正确,毕竟accessToken和accessToken的获取每天都有测试和时间限制,最好不要频繁请求。官方的校验工具地址是: https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
    4. 前台传过来的页面地址必须是当前页面除去'#'hash部分的链接,必须格外注意,否则自定义分享会不成功。

    还可以参考这篇教程来检查生成签名遇到的坑:

    微信分享invalid signature签名错误的坑

    后记

    本篇笔记仅用于学习交流使用,如有谬误,请不吝指正,谢谢!

    相关文章

      网友评论

          本文标题:nodejs后端写一个生成微信signature的接口

          本文链接:https://www.haomeiwen.com/subject/cqmjkqtx.html