详解nodejs微信jssdk后端接口

写过了两个微信的页面,遇到了挺多不会的问题,当时也是自己边查资料,边实践完成了简单的需求,刚好现在有空,把之前的东西整理一遍。

与普通的手机页面不同的是,微信页面提供给你了调用微信APP内置功能的接口,可以实现更复杂的功能。

jssdk的前端使用

  1. 前端页面调用jssdk首先要通绑定“公众号设置”的“功能设置”里填写“JS接口安全域名”
  2. 然后在页面中引入http://res.wx.qq.com/open/js/...
  3. 调用 wx.config({...}) 来验证权限配置
  4. 然后可根据需要 调用微信所提供的接口

后端返回接口

在前端调用时wx.config({...})中需要的参数需要我们自己进行返回

wx.config({
  debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  appId: '', // 必填,公众号的唯一标识
  timestamp: , // 必填,生成签名的时间戳
  nonceStr: '', // 必填,生成签名的随机串
  signature: '',// 必填,签名
  jsApiList: [] // 必填,需要使用的JS接口列表
});

其中 timestamp , nonceStr, signature,是需要后端计算返回的。

签名获取方法

签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。

而其中的 jsapi_ticket 是通过 access_token 来获取的,且两者都有过期时间(7200秒)其中 jsapi_ticket 更是限制了获取次数。所以为了保存两者,使用redis数据库保存在内存中是个很好的选择(可快速读取,并设置过期时间)。

token获取方法:

/**
 * 获取token
 * @return {promise} res 值为token
 */
function getToken () {
 return redis.getVal('token') // 首先读取 redis 是否存在token
  .then(function (res) {
   if (res === null) {  // 若不存在,则返回savetoken() 获取
    // console.log('不存在token 调用saveToken')
    return saveToken ()
   } else {       // 若存在 则直接返回
    // console.log('存在token 直接返回')
    return res
   }
  })
  .catch(function (err) {  // 捕获 错误
   console.log(err)
  })
}

/**
 * 从服务端获取token 并保存在redis中
 * @return {promise} 值 为 token
 */
function saveToken () {

 return new Promise((resolve, reject) => {

  let reqUrl = config.gettoken_url // https://api.weixin.qq.com/cgi-bin/token?
  let params = {
   grant_type: 'client_credential',
   appid: config.appid,
   secret: config.appsecret
  }

  let options = {
   method: 'get',
   url: reqUrl + qs.stringify(params)
  }

  request(options, function (err, res, body) {
   if (res) {
    let bodys = JSON.parse(body)
    let expires = bodys.expires_in
    let token = bodys.access_token

    redis.setKey('token', token, expires) // 将token 保存到 redis
     .catch(function (err) {
      console.log(err)
     })

    resolve(token)
   } else {
    reject(err)
   }
  })
 })
}

在配置文件中配置好所需要的appid和appsecret,首先查看redis中是否存在,如果存在就直接返回,没有的话,就调用saveToken去获取并保存在redis中

jsapi_ticket 获取方法

同理,jsapi_ticket 也采用同样的方式去获取

/**
 * 获取ticket
 * @return {promise} res 值为ticket
 */

function getJsTicket() { // 获取token
 return redis.getVal('ticket') // 首先读取 redis 是否存在ticket
  .then(function (res) {
   if (res === null) {  // 若不存在,则返回saveJsTicket() 获取
    // console.log('不存在ticket 调用saveJsTicket')
    return saveJsTicket ()
   } else {       // 若存在 则直接返回
    // console.log('存在ticket 直接返回')
    return res
   }
  })
  .catch(function (err) {  // 捕获 错误
   console.log(err)
  })
}

/**
 * 从服务端获取ticket 并保存在redis中
 * @return {promise} 值 为 ticket
 */
function saveJsTicket () {

 return new Promise((resolve, reject) => {

  getToken().then(function (token) {

   let reqUrl = config.ticket_start + token + config.ticket_end
   let options = {
    method: 'get',
    url: reqUrl
   }

   request(options, function (err, res, body) {
    if (res) {
     let bodys = JSON.parse(body)   // 解析微信服务器返回的
     let ticket = bodys.ticket    // 获取 ticket
     let expires = bodys.expires_in  // 获取过期时间

     redis.setKey('ticket', ticket, expires) // 将ticket 保存到 redis
      .catch(function (err) {
       console.log(err)
      })
     resolve(ticket)
    } else {
     reject(err)
    }
   })
  }).catch(function (err) {
    console.log(err)
  })
 })
}

签名算法

在获取jsapi_ticket后就可以生成JS-SDK权限验证的签名了

/**
 * 1. appId 必填,公众号的唯一标识
 * 2. timestamp 必填,生成签名的时间戳
 * 3. nonceStr 必填,生成签名的随机串
 * 4. signature 必填,签名
 */

const crypto = require('crypto')
const getJsTicket = require('./getJsTicket')
const config = require('../../config') // 微信设置

// sha1加密
function sha1(str) {
 let shasum = crypto.createHash("sha1")
 shasum.update(str)
 str = shasum.digest("hex")
 return str
}

/**
 * 生成签名的时间戳
 * @return {字符串}
 */
function createTimestamp () {
 return parseInt(new Date().getTime() / 1000) + ''
}

/**
 * 生成签名的随机串
 * @return {字符串}
 */
function createNonceStr () {
 return Math.random().toString(36).substr(2, 15)
}

/**
 * 对参数对象进行字典排序
 * @param {对象} args 签名所需参数对象
 * @return {字符串}  排序后生成字符串
 */
function raw (args) {
 var keys = Object.keys(args)
 keys = keys.sort()
 var newArgs = {}
 keys.forEach(function (key) {
  newArgs[key.toLowerCase()] = args[key]
 })

 var string = ''
 for (var k in newArgs) {
  string += '&' + k + '=' + newArgs[k]
 }
 string = string.substr(1)
 return string
}

/**
* @synopsis 签名算法
*
* @param jsapi_ticket 用于签名的 jsapi_ticket
* @param url 用于签名的 url ,注意必须动态获取,不能 hardcode
*
* @returns {对象} 返回微信jssdk所需参数对象
*/
function sign (jsapi_ticket, url) {
 var ret = {
  jsapi_ticket: jsapi_ticket,
  nonceStr: createNonceStr(),
  timestamp: createTimestamp(),
  url: url
 }
 var string = raw(ret)
 ret.signature = sha1(string)
 ret.appId = config.appid
 return ret
}

/**
 * 返回微信jssdk 所需参数对象
 * @param {字符串} url 当前访问URL
 * @return {promise}   返回promise类 val为对象
 */
function jsSdk (url) {
 return getJsTicket()
  .then(function (ticket) {
   return sign(ticket, url)
  })
  .catch(function (err) {
   console.log(err)
  })
}

function routerSdk (req, res, next) {
 let clientUrl = req.body.url
 if (clientUrl) {
  jsSdk(clientUrl)
   .then(function (obj) {
    res.json(obj)
   })
 } else {
  res.end('no url')
 }
}

module.exports = routerSdk

以上基本就完成了后端返回签名的过程(省略了redis部分)。具体细节可参考我当时的练手项目中的代码。

至此,前端就可以使用jssdk来完成功能的调用了。

ps:某次使用录音接口做了一个功能,但是发现,微信服务器只会保存3天数据,需要自己下载到自己的服务器才行,不知道诸位有没做过类似的需求,给我提供下指导啥的,感激不尽~

后记

后来又写过一个获取用户信息的页面,感觉也是挺常用的就写个demo出来看看吧(没有做access_token的保存,好像是没有获取次数限制)。

router.get('/', function(req, res, next){
 console.log("oauth - login")

 // 第一步:用户同意授权,获取code
 let router = 'get_wx_access_token'
 // 这是编码后的地址
 let return_uri = encodeURIComponent(base_url + router)
 console.log('回调地址:' + return_uri)
 let scope = 'snsapi_userinfo'

 res.redirect('https://open.weixin.qq.com/connect/oauth2/authorize?appid='+appid+'&redirect_uri='+return_uri+'&response_type=code&scope='+scope+'&state=STATE#wechat_redirect')
})

// 第二步:通过code换取网页授权access_token
router.get('/get_wx_access_token', function(req,res, next){
 console.log("get_wx_access_token")
 console.log("code_return: "+req.query.code)
 let code = req.query.code
 request.get(
  {
   url:'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' + appid + '&secret=' + appsecret+'&code=' + code + '&grant_type=authorization_code',
  },
  function(error, response, body){
   if(response.statusCode === 200){

    // 第三步:拉取用户信息(需scope为 snsapi_userinfo)
    // console.log(JSON.parse(body))

    let data = JSON.parse(body)
    let access_token = data.access_token
    let openid = data.openid

    request.get(
     {
      url:'https://api.weixin.qq.com/sns/userinfo?access_token='+access_token+'&openid='+openid+'&lang=zh_CN',
     },
     function(error, response, body){
      if(response.statusCode == 200){

       // 第四步:根据获取的用户信息进行对应操作
       let userinfo = JSON.parse(body)
       console.log(JSON.parse(body))
       console.log('获取微信信息成功!')

       小测试,实际应用中,可以由此创建一个帐户
       res.send("\
         <h1>"+userinfo.nickname+" 的个人信息</h1>\
         <p><img src='"+userinfo.headimgurl+"' /></p>\
         <p>"+userinfo.city+","+userinfo.province+","+userinfo.country+"</p>\
       ")

      }else{
       console.log(response.statusCode)
      }
     }
    )
   }else{
    console.log(response.statusCode)
   }
  }
 )
})

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

(0)

相关推荐

  • node.js实现微信JS-API封装接口的示例代码

    Wechat JS-API接口 功能: 用于管理和获取微信 JSSDK 生产的access_token.jsapi_ticket和签名(signature) Installation npm i wechat_interaction_jsapi Init let Jsapi = require("wechat_interaction_jsapi"); Usage WECHAT_APPID, WECHAT_APPSECRET 分别为开发者 id 和密码,在微信公众平台->开发-&g

  • 详解nodejs微信公众号开发——5.素材管理接口

    上一篇文章:nodejs微信公众号开发--4.自动回复各种消息,我们实现了被动回复文字和图文,回复图片失败,因为需要先获取通过素材管理接口上传多媒体文件而得到的MediaId,这一节们就来实现素材管理的接口.可参看:公众平台开发者文档 1. 新增临时素材 临时素材顾名思义是临时的,上传后一定时间就被清理掉,适用于一些有时效性的图文链接.关于临时素材需要注意的点: 对于临时素材,每个素材(media_id)会在开发者上传或粉丝发送到微信服务器3天后自动删除(所以用户发送给开发者的素材,若开发者需要

  • 详解nodejs微信jssdk后端接口

    写过了两个微信的页面,遇到了挺多不会的问题,当时也是自己边查资料,边实践完成了简单的需求,刚好现在有空,把之前的东西整理一遍. 与普通的手机页面不同的是,微信页面提供给你了调用微信APP内置功能的接口,可以实现更复杂的功能. jssdk的前端使用 前端页面调用jssdk首先要通绑定"公众号设置"的"功能设置"里填写"JS接口安全域名" 然后在页面中引入http://res.wx.qq.com/open/js/... 调用 wx.config({.

  • 详解基于Node.js的微信JS-SDK后端接口实现代码

    做了一个网站,放到线上,用微信打开,点击分享,可是分享后发给朋友的链接卡片是微信默认自带的,如下: 这标题,描述以及图片是默认自带的,丑不说,分享给别人还以为是盗号网站呢,而接入微信的JSSDK后,分享可以自定义内容,如下: 我承认,虽然这分享的标题和内容也并不正经,但这不妨碍我表达我们可以通过微信JSSDK定义分享内容,接下来我们将一步一步从零实现JSSDK从后端Node.js的接入. 成为测试公众号开发者 登录测试公众号后台 首先我们需要在微信公众平台申请测试接口,地址:https://mp

  • 详解nodejs微信公众号开发——2.自动回复

    上一篇文章:nodejs微信公众号开发(1)接入微信公众号,本篇文章将在此基础上实现简单的回复功能. 1. 接入代码的优化 之前我们简单粗暴的实现了微信公众号的接入,接入的代码直接写在了app.js文件里面,从项目开发的角度而言,不便于日后代码的维护,所以将这部分代码独立出来,按照koa的风格,写成一个中间件. 在根目录下新建wechat文件夹,新建generator.js文件, var sha1 = require('sha1'); module.exports = function(opts

  • 详解nodejs微信公众号开发——6.自定义菜单

    上一篇文章:nodejs微信公众号开发--5.素材管理接口,我们实现了新增临时素材.管理永久素材的接口,这些接口的实现,使我们能够推送多样的消息给用户.本节介绍的内容是关于自定义菜单 1. 自定义菜单的介绍 自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能.关于自定义菜单需要掌握以下几点内容: 自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单. 一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以"..."代替. 创建自定义菜单后,由于微信客

  • 详解nodejs微信公众号开发——3.封装消息响应模块

    上一篇文章:nodejs微信公众号开发(2)自动回复,实现了简单的关注回复.采用拼接字符串的形式,并不是很方便,这里我们将其封装承接口. 1. ejs模板引擎 不使用拼接字符串的方式,那么模板引擎就是较好的选择.Nodejs开源模板的选择很多,程序中使用 EJS,有Classic ASP/PHP/JSP的经验用起EJS来的确可以很自然,也就是说,你能够在 <%...%> 块中安排 JavaScript 代码,利用最传统的方式 <%=输出变量%>(另外 <%-输出变量是不会对

  • 详解nodejs微信公众号开发——4.自动回复各种消息

    上一篇文章:nodejs微信公众号开发--3.封装消息响应模块,实现了对消息接口的模块化处理,方便后期的使用,本篇文章将介绍微信公众号回复各种消息的功能实现,包括文本.图片.语音.视频.音乐.图文等. 注:感觉最近localtunnel很不稳定,测试起来比较麻烦,有条件的自己搞个云服务器吧,我比较偷懒,几继续使用localtunnel了. 1. 被动回复用户消息 当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML

  • 详解nodejs微信公众号开发——1.接入微信公众号

    接入微信公众号是开发的第一步,万事开头难,走好第一步,后面的路就更宽广. 1.公众平台测试帐号的使用 登录微信公众平台,由于很多开发人员并没有认证的的微信公众号,所以比较折中的方式是使用公众平台测试帐号来测试功能,其优势是能够测试微信公众号的绝大部分功能,不受认证门槛的限制. 进入测试账号管理界面: 我们需要配置接口的信息,URL和Token是微信公众平台和本地开发服务建立连接的桥梁. 微信配置好后,扫面下方的测试号二维码,即可以在手机上测试开发的功能. 2.验证公众号 一图胜千言.接入公众号时

  • 详解将微信小程序接口Promise化并使用async函数

    前言 小程序一直到现在接口还是和刚开始一样使用的回调函数的方式,如果想在小程序中不使用框架的情况下使用Promise+Async怎么办呢? 2019最新解决方案 1. 将接口Promise化 首先建一个文件wxPromise.js const promisify = name => option => { return new Promise((resolve, reject) => wx[name]({ ...option, success: resolve, fail: reject

  • 详解SpringBoot如何统一后端返回格式

    目录 为什么要对SpringBoot返回统一的标准格式 第一种:返回 String 第二种:返回自定义对象 第三种:接口异常 定义返回标准格式 高级实现方式 接口异常问题 SpringBoot为什么需要全局异常处理器 体验效果 全局异常接入返回的标准格式 今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何友好的返回统一的标准格式以及如何优雅的处理全局异常. 首先我们来看看为什么要返回统一的标准格式? 为什么要对SpringBoot返回统一的标准格式 在默认情况下,SpringB

随机推荐