详解Node.js access_token的获取、存储及更新


Posted in Javascript onJune 20, 2017

一、写在前面的话

上一篇文章中,我们使用 Node.js 成功的实现了接入微信公众平台功能。在这篇文章中,我们将实现微信公众平台一个非常重要的参数 access_token ,它是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用 access_token。

在开始之前,让我们先按捺住自己激动的心情、调整好呼吸,因为我们要将上一篇文章的代码重新整理一下。一个好的项目结构,更能有助于我们理清业务逻辑以及将来维护代码的便捷。OK?

二、整理项目结构

1.打开我们的项目,并在项目中添加文件夹,命名为 wechat ,如图:

详解Node.js access_token的获取、存储及更新

2.在 wechat 文件夹中添加文件并命名为 wechat.js。wechat.js 主要用于封装开发微信公众平台的所有方法。首先我们构建这个模块的结构,代码如下:

'use strict' //设置为严格模式

//构建 WeChat 对象 即 js中 函数就是对象
var WeChat = function(config){
  //设置 WeChat 对象属性 config
  this.config = config;
  
  //设置 WeChat 对象属性 token
  this.token = config.token;
}

//暴露可供外部访问的接口
module.exports = WeChat;

严格模式:是在 ECMAScript 5 中引入的概念。严格模式是为 Javascript 定义了一种解析与执行模型。

module.exports :暴露接口用于外部操作。实际上我们定义模块后,使用 node.js 的 require 引用时,node.js 会自动在我们定义的模块外层加入以下代码

/**
 * exports module.exports 的一个简短的引用
 * require 用于引入模块
 * module  当前模块的引用
 * __filename 当前模块的文件名
 * __dirname  当前模块的目录名
 */
(function (exports, require, module, __filename, __dirname) {
  //自定义模块的代码块
})();

相信对于有过 javascript 开发经验的同学,上面的代码并不陌生。我们可以将它理解为一个闭包,是一个匿名方法的调用,避免污染全局变量。

小知识:

在上面的代码中,除了我们所使用的 module.exports 对象,还有另一个用于暴露接口的 变量 exports (官方文档将 module.exports 称为对象,exports 称为 属性,我在这里也就这样称呼了),那么 module.exports 与 exports 有什么区别呢?

module.exports 对象是由模块系统创建的,exports 变量是在模块的文件级别作用域内有效的,它在模块被执行前被赋于 module.exports 的值。——来自Node.js官方文档

也就是说 exports 是 module.exports 的引用,而 module.exports 才是真正用于暴露接口的对象。 exports 赋值的所有属性与方法都赋值给了 module.exports 对象。

如果 module.exports 与 exports 将值赋值给了相同的属性,则按照赋值的先后顺序,取最后一个赋值;如果我们给 module.exports 赋值的是一个对象,则会覆盖 exports 的所有方法与属性。

因此我们在暴露接口的使用上,如果只是单一属性或方法的话,建议使用exports.属性/方法,要是导出多个属性或方法或使用对象构造方法,建议使用 module.exports。

3.为 WeChat 对象添加一个方法 auth,并将 app.js 中的验证方法粘贴进去

'use strict' //设置为严格模式

const crypto = require('crypto'); //引入加密模块

//构建 WeChat 对象 即 js中 函数就是对象
var WeChat = function(config){
  //设置 WeChat 对象属性 config
  this.config = config;

  //设置 WeChat 对象属性 token
  this.token = config.token;
}

/**
 * 微信接入验证
 */
WeChat.prototype.auth = function(req,res){
   //1.获取微信服务器Get请求的参数 signature、timestamp、nonce、echostr
    var signature = req.query.signature,//微信加密签名
      timestamp = req.query.timestamp,//时间戳
        nonce = req.query.nonce,//随机数
      echostr = req.query.echostr;//随机字符串

    //2.将token、timestamp、nonce三个参数进行字典序排序
    var array = [this.token,timestamp,nonce];
    array.sort();

    //3.将三个参数字符串拼接成一个字符串进行sha1加密
    var tempStr = array.join('');
    const hashCode = crypto.createHash('sha1'); //创建加密类型 
    var resultCode = hashCode.update(tempStr,'utf8').digest('hex'); //对传入的字符串进行加密

    //4.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
    if(resultCode === signature){
      res.send(echostr);
    }else{
      res.send('mismatch');
    }
}

//暴露可供外部访问的接口
module.exports = WeChat;

4.整理 app.js 文件的中的代码,如下:

const express = require('express'), //express 框架 
   wechat = require('./wechat/wechat'), 
    config = require('./config');//引入配置文件

var app = express();//实例express框架

var wechatApp = new wechat(config); //实例wechat 模块

//用于处理所有进入 3000 端口 get 的连接请求
app.get('/',function(req,res){
  wechatApp.auth(req,res);
});

//监听3000端口
app.listen(3000);

嗯!这样代码看着是不是舒服多了呢。机智如我

剩下的就是去微信公众平台接入验证了,在上一篇文章中有详细的教程,这里我就不再演示了

就是这么懒

三、access_token的获取、存储及更新

1.微信文档步骤

在开始码代码之前,我们依然是先理清实现的思路,在开始编写实现代码。打开 微信帮助文档 ,点击左侧菜单中的开始开发,点击其子菜单获取access_token,如图:

详解Node.js access_token的获取、存储及更新

详解Node.js access_token的获取、存储及更新

通过上面的 API 的描述,我们总结出以下步骤:

1.实现 https Get 请求

2.获取 access_token 并存储 如果 当前 access_token 过期则更新

2.access_token的获取、存储及更新 代码实现

整理好思路后我们就按照上一节的步骤去实现。通过帮助文档我们将用于请求微信API 的请求地址与参数,存放到 config.json 文件。

其中 appid 与 secret 两个参数 位于 微信公众平台 左侧菜单的基本配置中,如图:

详解Node.js access_token的获取、存储及更新

详解Node.js access_token的获取、存储及更新

开发者密码 点击重置,用手机微信扫面二维码后便可得到。config.json 代码如下

{
  "token":"wechat",
  "appID":"wx154f********764da",
  "appScrect":"59de4266*******8dbe9de4b798cd372",
  "apiDomain":"https://api.weixin.qq.com/",
  "apiURL":{
    "accessTokenApi":"%scgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
  }
}

由于微信 API 请求连接的域名是公用的,我们将它提出来,在请求地址中使用 %s(字符串) 占位符占位。

微信所有请求连接都是 https 协议,很幸运的是 Node.js 系统包中为我们提供了 https 的包,由于后面的请求会多次用到 https ,因此我们将它封装为一个公用的方法,以便以后的使用,再次打开 wechat.js 在构造方法中,引入 https 模块,并在构造函数内部添加 requestGet 方法

//用于处理 https Get请求方法
  this.requestGet = function(url){
    return new Promise(function(resolve,reject){
      https.get(url,function(res){
        var buffer = [],result = "";
        //监听 data 事件
        res.on('data',function(data){
          buffer.push(data);
        });
        //监听 数据传输完成事件
        res.on('end',function(){
          result = Buffer.concat(buffer,buffer.length).toString('utf-8');
          //将最后结果返回
          resolve(result);
        });
      }).on('error',function(err){
        reject(err);
      });
    });
  }

提示:

npm 提供了很多用于请求的工具包,比如 request ( 安装命令 npm install request ) 等。这里我只是用系统包去做请求处理。

由于 https 是异步请求的,我在这里面使用了 ES6 的 Promise 对象 。

完成了 requestGet方法后,我们的第1步骤也就完成了。下面开始第2步,获取 access_token 并存储 如果 当前 access_token 过期则更新。

在这之前我是想将 access_token 的存储位置依然放在 config.json 文件中,由于 access_token 在更新后 需要将文件重写,可能容易造成 config.json 文件的格式的紊乱,因此在 wechat 中重新创建一个 accessToken.json 文件用于存储 access_token

详解Node.js access_token的获取、存储及更新

{
  "access_token":"",
  "expires_time":0
}

其中 access_token 用于存储 我们 GET 请求后access_token 的值,expires_time 用于存储 access_token 的过期时间,保存为时间戳。

在 wechat.js 引入 fs 模块用于操作文件、util 工具模块用于处理占位符、 accessToken.json 文件

'use strict' //设置为严格模式

const crypto = require('crypto'), //引入加密模块
    https = require('https'), //引入 htts 模块
    util = require('util'), //引入 util 工具包
accessTokenJson = require('./access_token'); //引入本地存储的 access_token

//构建 WeChat 对象 即 js中 函数就是对象
var WeChat = function(config){
  //设置 WeChat 对象属性 config
  this.config = config;
  //设置 WeChat 对象属性 token
  this.token = config.token;
  //设置 WeChat 对象属性 appID
  this.appID = config.appID;
  //设置 WeChat 对象属性 appScrect
  this.appScrect = config.appScrect;
  //设置 WeChat 对象属性 apiDomain
  this.apiDomain = config.apiDomain;
  //设置 WeChat 对象属性 apiURL
  this.apiDomain = config.apiURL;

  //用于处理 https Get请求方法
  this.requestGet = function(url){
    return new Promise(function(resolve,reject){
      https.get(url,function(res){
        var buffer = [],result = "";
        //监听 data 事件
        res.on('data',function(data){
          buffer.push(data);
        });
        //监听 数据传输完成事件
        res.on('end',function(){
          result = Buffer.concat(buffer,buffer.length).toString('utf-8');
          //将最后结果返回
          resolve(result);
        });
      }).on('error',function(err){
        reject(err);
      });
    });
  }
}

在 wechat.js 添加获取 access_token 的方法 getAccessToken

/**
 * 获取微信 access_token
 */
WeChat.prototype.getAccessToken = function(){
  var that = this;
  return new Promise(function(resolve,reject){
    //获取当前时间 
    var currentTime = new Date().getTime();
    //格式化请求地址
    var url = util.format(that.apiURL.accessTokenApi,that.apiDomain,that.appID,that.appScrect);
    //判断 本地存储的 access_token 是否有效
    if(accessTokenJson.access_token === "" || accessTokenJson.expires_time < currentTime){
      that.requestGet(url).then(function(data){
        var result = JSON.parse(data); 
        if(data.indexOf("errcode") < 0){
          accessTokenJson.access_token = result.access_token;
          accessTokenJson.expires_time = new Date().getTime() + (parseInt(result.expires_in) - 200) * 1000;
          //更新本地存储的
          fs.writeFile('./wechat/access_token.json',JSON.stringify(accessTokenJson));
          //将获取后的 access_token 返回
          resolve(accessTokenJson.access_token);
        }else{
          //将错误返回
          resolve(result);
        } 
      });
    }else{
      //将本地存储的 access_token 返回
      resolve(accessTokenJson.access_token); 
    }
  });
}

在 app.js 中添加新的监听链接用于测试 我们获取的token

//用于请求获取 access_token
app.get('/getAccessToken',function(req,res){
  wechatApp.getAccessToken().then(function(data){
    res.send(data);
  });  
});

详解Node.js access_token的获取、存储及更新

这样我们就大功告成了!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
如何实现浏览器上的右键菜单
Jul 10 Javascript
js实现快速分享功能(你的文章分享工具)
Jun 25 Javascript
IE中JS跳转丢失referrer问题的2个解决方法
Jul 18 Javascript
分享我对JS插件开发的一些感想和心得
Feb 04 Javascript
Vue组件tree实现树形菜单
Apr 13 Javascript
React Native之TextInput组件解析示例
Aug 22 Javascript
使用vue + less 实现简单换肤功能的示例
Feb 21 Javascript
微信小程序时间标签和时间范围的联动效果
Feb 15 Javascript
webpack安装配置与常见使用过程详解(结合vue)
Jun 01 Javascript
vue实现PC端分辨率适配操作
Aug 03 Javascript
vue 实现锚点功能操作
Aug 10 Javascript
JavaScript 对象创建的3种方法
Nov 17 Javascript
详解angular 中的自定义指令之详解API
Jun 20 #Javascript
JS移动端/H5同时选择多张图片上传并使用canvas压缩图片
Jun 20 #Javascript
BootStrap Select清除选中的状态恢复默认状态
Jun 20 #Javascript
Vue实现路由跳转和嵌套
Jun 20 #Javascript
Vue.2.0.5实现Class 与 Style 绑定的实例
Jun 20 #Javascript
JS中的三个循环小结
Jun 20 #Javascript
详解Vue 方法与事件处理器
Jun 20 #Javascript
You might like
PHP 事件机制(2)
2011/03/23 PHP
PHP设计模式之调解者模式的深入解析
2013/06/13 PHP
jquery 入门教程 [翻译] 推荐
2009/08/17 Javascript
jQuery 幻灯片插件(带缩略图功能)
2011/01/24 Javascript
jquery Validation表单验证使用详解
2020/09/12 Javascript
基于jQuery实现响应式圆形图片轮播特效
2015/11/25 Javascript
jquery跟随屏幕滚动效果的实现代码
2016/04/13 Javascript
AngularJs Injecting Services Into Controllers详解
2016/09/02 Javascript
浅谈JavaScript的计时器对象
2016/12/26 Javascript
使用vue.js实现联动效果的示例代码
2017/01/10 Javascript
jQuery插件FusionCharts实现的3D帕累托图效果示例【附demo源码】
2017/03/25 jQuery
JavaScript实现一个空中避难的小游戏
2017/06/06 Javascript
vue 下列表侧滑操作实例代码详解
2018/07/24 Javascript
angular 服务随记小结
2019/05/06 Javascript
vue实现商品列表的添加删除实例讲解
2020/05/14 Javascript
vue等两个接口都返回结果再执行下一步的实例
2020/09/08 Javascript
[03:14]辉夜杯主赛事 12月25日每日之星
2015/12/26 DOTA
python 获取指定文件夹下所有文件名称并写入列表的实例
2018/04/23 Python
Python 从相对路径下import的方法
2018/12/04 Python
django mysql数据库及图片上传接口详解
2019/07/18 Python
Django ORM 常用字段与不常用字段汇总
2019/08/09 Python
python scipy卷积运算的实现方法
2019/09/16 Python
python如何获取apk的packagename和activity
2020/01/10 Python
python爬取网易云音乐热歌榜实例代码
2020/08/07 Python
python 密码学示例——理解哈希(Hash)算法
2020/09/21 Python
CSS3过渡transition效果实例介绍
2016/05/03 HTML / CSS
美国新娘礼品店:The Paisley Box
2020/09/08 全球购物
劳动模范事迹材料
2014/01/19 职场文书
办公室文员工作职责
2014/01/31 职场文书
个人委托书范本
2014/09/13 职场文书
关于运动会的广播稿
2014/09/22 职场文书
工作检讨书怎么写
2015/01/23 职场文书
秦兵马俑导游词
2015/02/02 职场文书
大学生毕业个人总结
2015/02/15 职场文书
单独二胎证明
2015/06/24 职场文书
《分数的意义》教学反思
2016/02/20 职场文书