微信网页登录逻辑与实现方法
现在的网站开发,都绕不开微信登录(毕竟微信已经成为国民工具)。虽然文档已经写得很详细,但是对于没有经验的开发者还是容易踩坑。
所以,专门记录一下微信网页认证的交互逻辑,也方便自己日后回查:
- 加载微信网页sdk
- 绘制登陆二维码:新tab页面绘制/本页面iframe绘制
- 用户扫码登陆,前端跳入回调网址
- 回调网址进一步做逻辑处理,如果是页内iframe绘制二维码,需要通知顶级页
微信网页SDK加载
在多人团队协作中,加载资源的代码需要格外小心。因为可能会有多个开发者在同一业务逻辑下调用,这会造成资源的重复加载。
处理方法有两种,第一种是对外暴露多余接口,专门check是否重复加载。但是考虑到调用者每次在加载前,都需要显式调用check()方法进行检查,难免会有遗漏。
所以采用第二种方法--设计模式中的缓存模式,代码如下:
//备忘录模式:防止重复加载 exportconstloadWeChatJs=(()=>{ letexists=false;//打点 constsrc='//res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js';//微信sdk网址 return()=>newPromise((resolve,reject)=>{ //防止重复加载 if(exists)returnresolve(window.WxLogin); letscript=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()即可。
//微信默认配置 constbaseOption={ self_redirect:true,//true:页内iframe跳转;false:新标签页打开 id:'wechat-container', appid:'wechat-appid', scope:'snsapi_login', redirect_uri:encodeURIComponent('//1.1.1.1/'), state:'', }; exportconstloadQRCode=(option,intl=false,width,height)=>{ const_option={...baseOption,...option}; returnnewPromise((resolve,reject)=>{ try{ window.WxLogin(_option); constele=document.getElementById(_option['id']); constiframe=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(){ constwxOption={ //... }; loadWeChatJs() .then(WxLogin=>loadQRCode(wxOption)) .catch(error=>console.log(`Error:${error.message}`)); }
回调网址与iframe通信
这一块我觉得是微信登陆交互中最复杂和难以理解的一段逻辑。开头有讲过,微信二维码渲染有2中方式,一种是打开新的标签页,另一种是在指定id的容器中插入iframe。
毫无疑问,第二种交互方式更友好,因为要涉及不同级层的页面通信,代码处理也更具挑战。
为了方便说明,请先看模拟的数据配置:
//redirect地址会被后端拿到,后端重定向到此地址,前端会访问此页面 //redirect地址中的参数,是前端人员留给自己使用的;后端会根据业务需要,添加更多的字段,然后一起返回前端 constquerystr='?'+stringify({ redirect:encodeURIComponent(`${window.location.origin}/account/redirect?`+stringify({ to:encodeURIComponent(window.location.origin), origin:encodeURIComponent(window.location.origin), state:'login' })), type:'login' }); constwxOption={ 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参数 constquerys=getQueryVariable(this.props.location.search); //step2:检查querys中的数据是否符合要求... //step3:向顶级页面传递消息 returnwindow.parent&&window.parent.postMessage('data','*'); }
至此,微信网页认证的流程完成。
更多:关于iframe通信的更多细节,请查看MDN的文档
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。