node.js实现微信开发之获取用户授权


Posted in Javascript onMarch 18, 2019

本篇主要讲述,如何在微信中打开自家页面后,弹窗请求用户授权,以便拿到用户的微信信息。

首先说一下,完成自定义分享信息的,从无到有的流程:

基础硬件服务:

需要一个公网可以访问的有效域名:

  • 购买域名,并备案,我是在阿里云购买的,备案需要十几个工作日。
  • 购买ip,然后设置上面的域名,解析到该ip,这个时间可以快到忽略。

拥有自己的服务器,来存放自己页面项目:

我还是在阿里云购买购买服务器,这个花费最大,几百元一年的使用权。
而且这个服务器,本质就是一台电脑,是电脑就有配置,我目前只是自己学习使用,配置几乎是最低的,而且购买的套餐自带公网ip,这么一来我连上面购买ip的钱也省了。
综上所述,最终我只购买了域名和一个套餐自带公网ip的服务器,服务器用来放置前端项目和后台项目。

阿里云ECS:https://cn.aliyun.com/product/ecs

微信公众平台,开发者认证

打开微信公众平台 https://mp.weixin.qq.com/,使用邮箱注册,注意,一个邮箱只能注册一个微信公众平台账号,一个账号只能选择一种账号分类且不能更改,这里一定要慎重,这里选择订阅号。

可选个人类型、企业类型等等,其中,个人类型是不没有分享定制功能的,但企业类型我又不符合。。。最终我还是选择的个人类型,因为即使我的账号没有权限,但微信公众号里面,提供一个功能全开的测试账号,使用测试账号可以进行学习和测试,还是没问题的。

填写信息,绑定微信,注册完成,登录进去。

为了进行开发,需要在这里和你的后台项目和前端项目进行对应的配置,让微信确认后台项目和前台项目都是你的之后,才会提供服务。

有关服务器端和后台项目的配置:

首先需要说明,由于订阅号的功能比较少,如果只是进行学习,建议在 开发 => 开发者工具中选择使用公众平台测试帐号进行学习性开发,这样可以使用全功能的微信服务,配置也比较少。
下面的配置步骤均是使用自己的账号需要进行的配置

  1. 开发 => 基本配置 => 公众号开发信息,在这里记下开发者ID(AppID),接着开通服务,记下开发者密码(AppSecret),开发中会需要输入。
  2. 设置IP白名单,这里写的是自己的服务器IP地址,因为功能上线后,需要使用这台服务器,通过开发者ID和密码来向微信服务区获取自己服务的access_token
  3. 进行下面的后台项目,目的是让微信确定这个后台项目是你的,检验方法是微信发起一个get请求,你返回正确的返回值,启用此配置时调用:
    1. url:接口地址,比如http://wx.my.com/forWx
    2. Token:完全自定义的一个字符串,相当于个暗号,你的返回值需要这个字符串参与拼装。
    3. EncodingAESKey:随机生成即可
    4. 消息加解密方式:自选,这里我使用的是明文模式

有关前端项目的配置:

设置 => 公众号设置 => 功能设置 => JS接口安全域名在此处添加你的要使用微信sdk功能的网站的域名,比如wx.qq.com或者wx.qq.com/user,最多可写三个,且需要验证。

> 验证的方式,就是将一个微信提供的txt文件,放在此域名对应的放置于服务器中的web项目的访问根目录中,需要和主文件(大部分默认为```index.html```)放在同一级,当提交的时候,微信会进行访问,来获取文件,确认此域名是你的。

配置完成后,就可以进行开发了。

下面进入代码阶段。

证明后台项目和前端项目时我自己的

首先,上面证明服务是自己的部分,我们需要实现一个接口,我用http://wx.my.com/forWx打的比方,那么为了启用配置,我需要实现/forWx给微信调用,下面是代码:

node的基础环境搭建省略,这里只写接口内部方法了,关键是参数加密拼装

const crypto = require('crypto') // 引入加密模块
const config = require('./config') // 引入配置文件
// 提供给微信调用
server.get('/forWx', function (req, res) {
 res.header('Access-Control-Allow-Origin', '*')
 // 1.获取微信服务器Get请求的参数 signature、timestamp、nonce、echostr
 let signature = req.query.signature // 微信加密签名
 let timestamp = req.query.timestamp // 时间戳
 let nonce = req.query.nonce // 随机数
 let echostr = req.query.echost // 随机字符串

 // 2.将token、timestamp、nonce三个参数进行字典序排序,其中token就是设置在微信页面中的那个自定义字符串
 let array = [config.token, timestamp, nonce]
 array.sort()

 // 3.将三个参数字符串拼接成一个字符串进行sha1加密
 let tempStr = array.join('')
 const hashCode = crypto.createHash('sha1') //创建加密类型 
 let resultCode = hashCode.update(tempStr, 'utf8').digest('hex')
 
 //4.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
 if (resultCode === signature) {
 res.send(echostr)
 } else {
 res.send('mismatch')
 }
})

完成,上面是证明服务器是我的,后面还需要证明前端项目是我的,这个就跳过了,因为太简单,直接下载那个文件,放到自己服务器中,前端项目的index.html同级即可

上面的操作,是只要想进行微信公页面开发,必须要有的步骤,一切的基础。

首先顺着功能使用流程,顺一下实现此功能的方法:

用户在微信打开页面后,立即或者通过方法触发ajax,把当前url和一些state(自定义的数据,因为弹窗请求用户授权,是需要跳转页面的,这个state就是会帮你带到下个页面链接中的数据)作为请求参数,请求自己的后台接口。
后台请求微信服务器,把以下作为参数,拼装到某个固定的微信指定的url后,返回给前端,参数为:

  • appId:自己的AppId
  • redirect_uri:前端给的url
  • scope:授权方式,是静默授权(只能获取用户openId)还是弹窗授权(能获取用户微信个人信息)
  • state:要带到新页面的参数

前端拿到后端拼好的这个url,直接window.location.href暴力跳转

如果静默授权,则直接用户无感,如果是弹窗授权,则新页面(微信方提供的页面)会弹窗询问用户,是否授权

用户同意授权后,微信再次跳转页面,即跳转到之前传的你的url地址中,还会把state参数给你带上,此外,还多了个code参数,即openId

新页面中,可以使用用户的openId,再加上自己的AppId和AppSecret,调用微信的接口,获取用户的access_token

最后再使用用户的openId和access_token,成功获取用户信息

下面是前端获取微信授权的...html页面

<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <!-- 页面描述 -->
 <meta name="description" content=""/>
 <!-- 页面关键词 -->
 <meta name="keywords" content="" />
 <!-- 搜索引擎抓取 -->
 <meta name="robots" content="index,follow"/>
 <!-- 启用360浏览器的极速模式(webkit) -->
 <meta name="renderer" content="webkit">
 <!-- 避免IE使用兼容模式 -->
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <!-- 不让百度转码 -->
 <meta http-equiv="Cache-Control" content="no-siteapp"/>
 <!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 -->
 <meta name="HandheldFriendly" content="true">
 <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
 <!-- 优先使用 IE 最新版本和 Chrome -->
 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
 <meta name="apple-mobile-web-app-capable" content="yes">
 <meta name="mobile-web-app-capable" content="yes">
 <link rel="shortcut icon" type="image/x-icon" href="../static/favicon.ico" rel="external nofollow" >
 <title>微信</title>
 <style>
 html, body {
  background-color: skyblue;
  font-size: 16px;
  height: 50%;
  width: 100%;
 }
 #index {
  padding: 10px;
 }
 #index .box > div {
  cursor: pointer;
  background-color: #fff;
  display: inline-block;
  padding: 5px;
  margin: 10px;
 }
 #index .box .getUserInfo {
  display: none;
 }
 </style>
 <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
 crossorigin="anonymous"></script>
 <script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
</head>
<body>
 <div id="index">
 <div class="box">
  <div class="initOauth2" type="snsapi_base">获取微信授权(静默)</div>
  <div class="initOauth2" type="snsapi_userinfo">获取微信授权(弹框)</div>
  <br>
  <div class="wxSweep">扫一扫</div>
  <br>
  <div class="getUserInfo">获取用户信息</div>
 </div>
 <div class="userInfo"></div>
 </div>
</body>
<script>
 let BASE_URL = 'http://wxtestapi.junlli.com'

 // 获取 url 参数
 const getValue = () => {
 let flag = decodeURI(window.location.search.substr(1));
 if (!flag) return undefined
 let arr = flag.split('&')
 if (arr.length <= 0) return undefined
 let obj = {}
 for (let i = 0; i < arr.length; i++) {
  let tempArr = arr[i].split('=')
  obj[tempArr[0]] = tempArr[1]
 }
 return obj
 }

 let urlParams = getValue()
 let code
 // 判断是否有code
 if (urlParams && urlParams.code) {
 code = urlParams.code
 $('.getUserInfo').css('display', 'inline-block')
 }

 $('.getUserInfo').on('click', function() {
 if (!code) return alert('请重新获取授权')
 $.ajax({
  url: BASE_URL + '/getUserInfo',
  type: 'post',
  data: {
  code,
  },
  success: function(data) {
  console.log(data)
  $('.userInfo').html(JSON.stringify(data))
  },
  error: function(error) {
  console.log(error)
  alert('请重新获取授权')
  }
 })
 })

 // 获取微信授权
 $('.box .initOauth2').on('click', function() {
 wxInitOauth2($(this).attr('type'))
 })
 // 初始化 微信授权
 wxInitOauth2 = type => {
 let url = window.location.origin + window.location.pathname
 console.log('url', url)
 $.ajax({
  url: BASE_URL + '/getOauth2',
  type: 'post',
  data: {
  url,
  type,
  state: 'abcde'
  },
  success: function(data) {
  // 去跳转
  window.location.href = data.url
  // console.log(data)
  },
  error: function(error) {
  console.log(error)
  },
 })
 }
</script>
</html>

下面是node后台代码

const config = require('./config') // 引入配置文件

// 通过 code 获取用户的 openId 和 access_token
const getOpenIdAndAccessToken = code => {
 let params = {
 appid: config.appId,
 secret: config.appSecret,
 code,
 grant_type: 'authorization_code'
 }
 let url = `https://api.weixin.qq.com/sns/oauth2/access_token?${qs.stringify(params)}`
 return new Promise((resolve, reject) => {
 request(url, function (error, res, body) {
  if (res) {
  let bodyObj = JSON.parse(body)
  resolve(bodyObj);
  } else {
  reject(error);
  }
 })
 })
}

// 获取用户信息
const getUserInfo = ({ access_token, openid }) => {
 let params = {
 access_token,
 openid,
 lang: 'zh_CN'
 };
 let url = `https://api.weixin.qq.com/sns/userinfo?${qs.stringify(params)}`
 return new Promise((resolve, reject) => {
 request(url, function (err, res, body) {
  if (res) {
  resolve(JSON.parse(body))
  } else {
  reject(err);
  }
 });
 })
}

// 获取微信授权 --- code
server.post('/getOauth2', (req, res) => {
 try {
 let params = req.body
 let redirect_uri = params.url
 let state = params.state
 let type = params.type
 // 第一步:用户同意授权,获取code
 // type:snsapi_base // 不弹出授权页面,直接跳转,只能获取用户openid
 // type:snsapi_userinfo // 弹出授权页面,可通过openid拿到昵称、性别、所在地
 var scope = type // 弹出授权页面,拿到code
 let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${config.appId}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}${state ? '&state=' + state : ''}#wechat_redirect`
 res.send({ url });
 } catch (error) {
 res.send(error)
 }
})


// 获取用户个人信息
server.post('/getUserInfo', (req, res) => {
 try {
 let params = req.body
 let code = params.code
 // 先用 code 换取 openId 和 access_token
 getOpenIdAndAccessToken(code).then(obj => {
  // 用 openId 和 access_token 获取个人信息
  getUserInfo(obj).then(data => {
  res.send(data)
  }).catch(error => res.send(error))
 }).catch(error => res(error))
 } catch (error) {
 res.send(error)
 }
})

整体功能实现的步骤和具体代码如上,请酌情参考。

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

Javascript 相关文章推荐
《JavaScript高级程序设计》阅读笔记(一) ECMAScript基础
Feb 27 Javascript
深入理解JavaScript 闭包究竟是什么
Apr 12 Javascript
如何用JavaScript动态呼叫函数(两种方式)
May 03 Javascript
jQuery实现在下拉列表选择时获取json数据的方法
Apr 16 Javascript
jQuery实现的简单折叠菜单(折叠面板)效果代码
Sep 16 Javascript
jQuery 获取跨域XML(RSS)数据的相关总结分析
May 18 Javascript
Reactjs实现通用分页组件的实例代码
Jan 19 Javascript
利用JS实现文字的聚合动画效果
Jan 22 Javascript
Javascript中的prototype与继承
Feb 06 Javascript
jQuery实现checkbox的简单操作
Nov 18 jQuery
React Native中Mobx的使用方法详解
Dec 04 Javascript
jquery html添加元素/删除元素操作实例详解
May 20 jQuery
学习node.js 断言的使用详解
Mar 18 #Javascript
React 使用Hooks简化受控组件的状态绑定
Mar 18 #Javascript
JavaScript显式数据类型转换详解
Mar 18 #Javascript
浅谈js中的bind
Mar 18 #Javascript
node微信开发之获取access_token+自定义菜单
Mar 17 #Javascript
JavaScript中this用法学习笔记
Mar 17 #Javascript
通过JavaScript下载文件到本地的方法(单文件)
Mar 17 #Javascript
You might like
根德YB400的电路分析
2021/03/02 无线电
phpmyadmin安装时提示:Warning: require_once(./libraries/common.inc.php)错误解决办法
2011/08/18 PHP
php获取本地图片文件并生成xml文件输出具体思路
2013/04/27 PHP
利用php获取服务器时间的实现代码
2013/06/07 PHP
destoon复制新模块的方法
2014/06/21 PHP
[推荐]javascript 面向对象技术基础教程
2009/03/03 Javascript
js 判断浏览器使用的语言示例代码
2014/03/22 Javascript
JS获取Table中td值的方法
2015/03/19 Javascript
drag-and-drop实现图片浏览器预览
2015/08/06 Javascript
Angularjs整合微信UI(weui)
2016/03/15 Javascript
Jquery组件easyUi实现手风琴(折叠面板)示例
2016/08/23 Javascript
jQuery无缝轮播图代码
2016/12/22 Javascript
bootstrap multiselect 多选功能实现方法
2017/06/05 Javascript
移动端效果之Swiper详解
2017/10/09 Javascript
vue计算属性和监听器实例解析
2018/05/10 Javascript
[03:57]《不朽》——2015DOTA2国际邀请赛—中国军团出征主题曲MV
2015/07/15 DOTA
Python 迭代器工具包【推荐】
2016/05/06 Python
Anaconda下安装mysql-python的包实例
2018/06/11 Python
Python中单线程、多线程和多进程的效率对比实验实例
2019/05/14 Python
python3.6 tkinter实现屏保小程序
2019/07/30 Python
Python3监控windows,linux系统的CPU、硬盘、内存使用率和各个端口的开启情况详细代码实例
2020/03/18 Python
Python 通过正则表达式快速获取电影的下载地址
2020/08/17 Python
使用Python实现NBA球员数据查询小程序功能
2020/11/09 Python
基于Html5实现的语音搜索功能
2019/05/13 HTML / CSS
澳大利亚自然和有机的健康美容产品一站式商店:Ziani Beauty
2017/12/28 全球购物
欧洲最大的拼图游戏商店:JigsawPuzzle.co.uk
2018/07/04 全球购物
美国家庭鞋店:Shoe Sensation
2019/09/27 全球购物
怎样让char类型的东西转换成int类型
2013/12/09 面试题
Java里面如何创建一个内部类的实例
2015/01/19 面试题
法制宣传标语
2014/06/23 职场文书
大学毕业生个人总结
2015/02/28 职场文书
学习新党章心得体会2016
2016/01/15 职场文书
大学自主招生自荐信(2016精选篇)
2016/01/28 职场文书
Python自动化测试PO模型封装过程详解
2021/06/22 Python
php修改word的实例方法
2021/11/17 PHP
MySQL利用UNION连接2个查询排序失效详解
2021/11/20 MySQL