微信网页登录逻辑与实现方法


Posted in Javascript onApril 29, 2019

现在的网站开发,都绕不开微信登录(毕竟微信已经成为国民工具)。虽然文档已经写得很详细,但是对于没有经验的开发者还是容易踩坑。

所以,专门记录一下微信网页认证的交互逻辑,也方便自己日后回查:

  1. 加载微信网页sdk
  2. 绘制登陆二维码:新tab页面绘制 / 本页面iframe绘制
  3. 用户扫码登陆,前端跳入回调网址
  4. 回调网址进一步做逻辑处理,如果是页内iframe绘制二维码,需要通知顶级页

微信网页SDK加载

在多人团队协作中,加载资源的代码需要格外小心。因为可能会有多个开发者在同一业务逻辑下调用,这会造成资源的重复加载。

处理方法有两种,第一种是对外暴露多余接口,专门check是否重复加载。但是考虑到调用者每次在加载前,都需要显式调用check()方法进行检查,难免会有遗漏。

所以采用第二种方法--设计模式中的缓存模式,代码如下:

// 备忘录模式: 防止重复加载
export const loadWeChatJs = (() => {
 let exists = false; // 打点
 const src = '//res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'; // 微信sdk网址

 return () => new Promise((resolve, reject) => {
  // 防止重复加载
  if(exists) return resolve(window.WxLogin);
  
  let script = document.createElement('script');
  script.src = src;
  script.type = 'text/javascript';
  script.onerror = reject; // TODO: 失败时候, 可以移除script标签
  script.onload = () => {
   exists = true;
   resolve(window.WxLogin);
  };
  document.body.appendChild(script);
 });
})();

绘制登陆二维码

根据《微信登陆开发指南》,将参数传递给window.WxLogin()即可。

// 微信默认配置
const baseOption = {
 self_redirect: true, // true: 页内iframe跳转; false: 新标签页打开 
 id: 'wechat-container', 
 appid: 'wechat-appid',
 scope: 'snsapi_login',
 redirect_uri: encodeURIComponent('//1.1.1.1/'),
 state: '',
};

export const loadQRCode = (option, intl = false, width, height) => {
 const _option = {...baseOption, ...option};

 return new Promise((resolve, reject) => {
  try {
   window.WxLogin(_option);
   const ele = document.getElementById(_option['id']);
   const iframe = ele.querySelector('iframe');
   iframe.width = width? width : '300';
   iframe.height = height? height : '420'; 
   // 处理国际化
   intl && (iframe.src = iframe.src + '&lang=en');
   resolve(true);
  } catch(error) {
   reject(error);
  }
 });
};

在需要使用的业务组件中,可以在周期函数componentDidMount调用,下面是demo代码:

componentDidMount() {
  const wxOption = {
    // ...
  };
  loadWeChatJs()
    .then(WxLogin => loadQRCode(wxOption))
    .catch(error => console.log(`Error: ${error.message}`));  
}

回调网址与iframe通信

这一块我觉得是微信登陆交互中最复杂和难以理解的一段逻辑。开头有讲过,微信二维码渲染有2中方式,一种是打开新的标签页,另一种是在指定id的容器中插入iframe。

毫无疑问,第二种交互方式更友好,因为要涉及不同级层的页面通信,代码处理也更具挑战。

为了方便说明,请先看模拟的数据配置:

// redirect 地址会被后端拿到, 后端重定向到此地址, 前端会访问此页面
// redirect 地址中的参数, 是前端人员留给自己使用的; 后端会根据业务需要, 添加更多的字段, 然后一起返回前端
const querystr = '?' + stringify({
 redirect: encodeURIComponent(`${window.location.origin}/account/redirect?` + stringify({
  to: encodeURIComponent(window.location.origin),
  origin: encodeURIComponent(window.location.origin),
  state: 'login'
 })),
 type: 'login'
});

const wxOption = {
 id: 'wechat-container',
 self_redirect: true,
 redirect_uri: encodeURIComponent(`//1.1.1.1/api/socials/weixin/authorizations${querystr}`) // 微信回调请求地址
};

前后端、微信服务器、用户端交互逻辑

按照上面的配置,我描述一下前端、用户端、微信服务器和后端交互的逻辑:

  • 前端根据wxOption加载了二维码,所有信息都放在了二维码中。同时监听微信服务器的消息。
  • 用户手机扫码,通知微信服务器确定登陆。
  • 微信服务器接受到用户的扫码请求,转发给前端。
  • 前端收到微信服务器传来消息,根据wxOption的redirect_uri参数,跳转到此url地址。注意:
    • 这个接口地址是后端的,请求方式是GET
    • 前端通过拼接params携带参数
    • 地址会被拼接微信服务器传来的一个临时token,用于交给后端换取用户公众密钥
  • 后端接收到/api/socials/weixin/authorizations${querystr}的请求,decode解码querystr中的信息。然后向微信服务端请求用户公众密钥。根绝前后端的约定(demo中用的是redirect字段),重定向到前端指定的redirect字段,并且拼接用户公众密钥等更多信息。
  • 前端知悉重定向,跳到重定向的路由(demo中用的是/account/redirect)
  • 在对应的路由处理后端传来的用户密钥等数据即可
  • 至此,微信认证的四端交互逻辑完成

跨Iframe通信

前面流程走完了,现在的情况是页面中iframe的二维码区域,已经被替换成了/account/redirect?...的内容。

为了实现通信,需要在页面的周期中监听message事件,并在组件卸载时,卸载此事件:

componentDidMount() {
 // ... ...
 
 window.addEventListener('message', this.msgReceive, false);
}

componentWillUnmount() {
 window.removeEventListener('message', this.msgReceive);
}

msgReceive(event) {
 // 监测是否是安全iframe
 if(!event.isTrusted) {
  return;
 }
 console.log(event.data); // 获取iframe中传来的数据, 进一步进行逻辑处理
}

而在/account/redirect?...路由对应的组件中,我们需要解析路由中的params参数,按照业务逻辑检查后,将结果传递给前面的页面:

componentDidMount() {
  // step1: 获取url中params参数
  const querys = getQueryVariable(this.props.location.search);
  // step2: 检查querys中的数据是否符合要求 ... 
  // step3: 向顶级页面传递消息
  return window.parent && window.parent.postMessage('data', '*');
}

至此,微信网页认证的流程完成。

更多:关于iframe通信的更多细节,请查看MDN的文档

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

Javascript 相关文章推荐
JS 获取select(多选下拉)中所选值的示例代码
Aug 02 Javascript
javascript eval(func())使用示例
Dec 05 Javascript
php读取sqlite数据库入门实例代码
Jun 25 Javascript
Javascript访问器属性实例分析
Dec 30 Javascript
详解Bootstrap按钮
Jan 04 Javascript
深入剖析JavaScript:Object类型
May 10 Javascript
用js动态添加html元素,以及属性的简单实例
Jul 19 Javascript
jquery实现简单的瀑布流布局
Dec 11 Javascript
bootstrap table配置参数例子
Jan 05 Javascript
jQuery插件FusionCharts绘制的3D饼状图效果实例【附demo源码下载】
Mar 03 Javascript
用户管理的设计_jquery的ajax实现二级联动效果
Jul 13 jQuery
在vue中v-bind使用三目运算符绑定class的实例
Sep 29 Javascript
vue和better-scroll实现列表左右联动效果详解
Apr 29 #Javascript
Vue 引入AMap高德地图的实现代码
Apr 29 #Javascript
微信小程序--获取用户地理位置名称(无须用户授权)的方法
Apr 29 #Javascript
Vuejs学习笔记之使用指令v-model完成表单的数据双向绑定
Apr 29 #Javascript
微信小程序-form表单提交代码实例
Apr 29 #Javascript
vue在index.html中引入静态文件不生效问题及解决方法
Apr 29 #Javascript
微信小程序传值以及获取值方法的详解
Apr 29 #Javascript
You might like
模拟xcopy的函数
2006/10/09 PHP
Fedora下安装php Redis扩展笔记
2014/09/03 PHP
两款万能的php分页类
2015/11/12 PHP
PHP数据对象PDO操作技巧小结
2016/09/27 PHP
PHP简单数据库操作类实例【支持增删改查及链式操作】
2016/10/10 PHP
PHP获取数组中单列值的方法
2017/06/10 PHP
php7连接MySQL实现简易查询程序的方法
2020/10/13 PHP
php中get_object_vars()在数组的实例用法
2021/02/22 PHP
Javascript实例教程(19) 使用HoTMetal(2)
2006/12/23 Javascript
jquery实现倒计时代码分享
2014/06/13 Javascript
初始Nodejs
2014/11/08 NodeJs
jQuery实现信息提示框(带有圆角框与动画)效果
2015/08/07 Javascript
jQuery简单实现input文本框内灰色提示文本效果的方法
2015/12/02 Javascript
jquery UI Datepicker时间控件的使用及问题解决
2016/04/28 Javascript
jQuery插件formValidator实现表单验证
2016/05/23 Javascript
JavaScript中输出信息的方法(信息确认框-提示输入框-文档流输出)
2016/06/12 Javascript
利用jQuery插件imgAreaSelect实现获得选择域的图像信息
2016/12/02 Javascript
Servlet实现文件上传,可多文件上传示例
2016/12/05 Javascript
解析ajaxFileUpload 异步上传文件简单使用
2016/12/30 Javascript
BootStrap 图片样式、辅助类样式和CSS组件的实例详解
2017/01/20 Javascript
详解使用PM2管理nodejs进程
2017/10/24 NodeJs
vue vuex vue-rouert后台项目——权限路由(适合初学)
2017/12/29 Javascript
一个Vue页面的内存泄露分析详解
2018/06/25 Javascript
详解React 的几种条件渲染以及选择
2018/10/23 Javascript
Nuxt.js之自动路由原理的实现方法
2018/11/21 Javascript
Python for Informatics 第11章之正则表达式(二)
2016/04/21 Python
Python模拟随机游走图形效果示例
2018/02/06 Python
Python使用爬虫爬取静态网页图片的方法详解
2018/06/05 Python
python 随机生成10位数密码的实现代码
2019/06/27 Python
keras绘制acc和loss曲线图实例
2020/06/15 Python
Django中如何用xlwt生成表格的方法步骤
2021/01/31 Python
顶丰TOPPIK台湾官网:增发纤维假发,告别秃发困扰
2018/06/13 全球购物
Sneaker Studio捷克:购买运动鞋
2018/07/08 全球购物
Agoda中文官网:安可达(低价预订全球酒店)
2021/01/18 全球购物
三严三实对照检查材料思想汇报
2014/09/28 职场文书
使用CSS自定义属性实现骨架屏效果
2022/06/21 HTML / CSS