详解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 相关文章推荐
JMenuTab简单使用说明
Mar 13 Javascript
JS getStyle获取最终样式函数代码
Apr 01 Javascript
js操作时间(年-月-日 时-分-秒 星期几)
Jun 20 Javascript
JavaScript语言核心数据类型和变量使用介绍
Aug 23 Javascript
js实现鼠标感应图片展示的方法
Feb 27 Javascript
JavaScript 冒泡排序和选择排序的实现代码
Sep 03 Javascript
canvas 画布在主流浏览器中的尺寸限制详细介绍
Dec 15 Javascript
纯js仿淘宝京东商品放大镜功能
Mar 02 Javascript
详解cordova打包成webapp的方法
Oct 18 Javascript
JavaScript常见JSON操作实例分析
Aug 08 Javascript
vue中实现点击空白区域关闭弹窗的两种方法
Dec 30 Vue.js
JS数组索引检测中的数据类型问题详解
Jan 11 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 php_openssl.dll的作用
2013/07/01 PHP
利用中国天气预报接口实现简单天气预报
2014/01/20 PHP
PHP的魔术常量__METHOD__简介
2014/07/08 PHP
CI框架学习笔记(二) -入口文件index.php
2014/10/27 PHP
Laravel 5框架学习之用户认证
2015/04/09 PHP
PHP闭包函数传参及使用外部变量的方法
2016/03/15 PHP
jQuery 可以拖动的div实现代码 脚本之家修正版
2009/06/26 Javascript
基于node.js的快速开发透明代理
2010/12/25 Javascript
为指定的元素添加遮罩层的示例代码
2014/01/15 Javascript
关于事件mouseover ,mouseout ,mouseenter,mouseleave的区别
2015/10/12 Javascript
jQuery焦点图插件SaySlide
2015/12/21 Javascript
js创建对象几种方式的优缺点对比
2016/09/28 Javascript
nodejs 搭建简易服务器的图文教程(推荐)
2017/07/18 NodeJs
关于Stream和Buffer的相互转换详解
2017/07/26 Javascript
ES6 javascript中Class类继承用法实例详解
2017/10/30 Javascript
微信小程序中换行空格(多个空格)写法详解
2018/07/10 Javascript
微信小程序实现签到功能
2018/10/31 Javascript
基于JS实现父组件的请求服务过程解析
2019/10/14 Javascript
Vuex的热更替如何实现
2020/06/05 Javascript
Python中正则表达式的用法实例汇总
2014/08/18 Python
Python设计模式编程中Adapter适配器模式的使用实例
2016/03/02 Python
浅谈Python数据类型判断及列表脚本操作
2016/11/04 Python
Python3获取拉勾网招聘信息的方法实例
2019/04/03 Python
OpenCV4.1.0+VS2017环境配置的方法步骤
2020/07/09 Python
css3发光搜索表单分享
2014/04/11 HTML / CSS
澳大利亚排名第一的露营和户外设备在线零售商:Outbax
2020/05/06 全球购物
Linux如何为某个操作添加别名
2015/02/05 面试题
应届生简历中的自我评价
2014/01/13 职场文书
2014年幼儿园植树节活动方案
2014/03/02 职场文书
求职意向书
2014/04/01 职场文书
起诉书格式范文
2015/05/20 职场文书
2016年情人节问候语
2015/11/11 职场文书
竞聘演讲报告:基本写作有哪些?附开头范文
2019/10/16 职场文书
python xlwt模块的使用解析
2021/04/13 Python
详解用Python把PDF转为Word方法总结
2021/04/27 Python
MySQL范围查询优化的场景实例详解
2022/06/10 MySQL