当前位置: 首页 > news >正文

微信全局登录设计与实现

前言

最近对于工程的微信登录逻辑进行了重构,进行了如下思考。

工程简介

工程是一个后台管理系统,使用vue框架客户端渲染,其中有一个模块运行在手机端,并且涉及到微信登录,在早期的设计中,对于微信浏览器内的登录是这样处理的:

  • 微信浏览器或者普通浏览器打开分享的链接
  • 如果是非登录状态,那么在请求接口的过程中因为网络请求拦截器的作用会将页面重定向到登录页
  • 在登录页进行判断,如果是普通浏览器则手动登录,微信浏览器则自动登录
  • 登录成功后会重定向到之前的页面

为什么之前会这样处理呢,原因在于,此工程的登录流程会设计到多个状态的查询与处理,例如:

  • 微信登录后检查用户信息,如果用户没有绑定手机号,那么弹出绑定手机号的模态框进行绑定手机号
  • 手机号码登录后,如果用户没有绑定微信,那么弹出微信绑定的模态框,要求用户绑定微信

这样过于复杂的一系列需求,不太利于做成全局的处理,当时也没想到好的解决方案,但是这样来回的跳转,对于用户体验不够友好,近期对项目进行重构时,决心改善这一问题。

解决方案

提炼公共逻辑

对于微信登录,获取微信授权链接的跳转以及微信返回code的处理是一个通用的逻辑,对此做了如下封装

export function wxLogin(callback) {
    const code = getQueryString(window.location.href, 'code')
    if (!code) {
        window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${process.env.WX_APP_ID}&response_type=code&scope=snsapi_userinfo&state=onfuckweixin&redirect_uri=${escape(`${window.location.href}`)}#wechat_redirect`
    } else {
        axios.post(IP + '/login/wxauth', {
            code: code
        }).then(data => {
            if (!data.phone_bind) {
                bindPhone()
            } else {
                location.href = replaceQueryString('code', '')
                callback()
            }
        }).catch(e => {
            vue.$notify.error({
                title: '错误',
                message: '微信登陆失败'
            })
        })
    }

    function bindPhone() {
        const comp = new BindComponent().$mount()
        document.body.appendChild(comp.$el)
        comp.open({
            type: 'phone',
            success: () => {
                location.href = replaceQueryString('code', '')
                window.location.reload()
            }
        })
    }
}
复制代码

大致的意思是,进行微信登录时,先判断url是否已经有code,有code说明已经获得授权,那么直接将code发送给服务器进行登录就好,如果没有code信息,那么说明是未授权,则跳转微信授权地址,获取授权,值得一题的是这一段逻辑:

import Vue from 'vue'
import Bind from '@/components/bind/bind'
const BindComponent = Vue.extend(Bind)

...

function bindPhone() {
    const comp = new BindComponent().$mount()
    document.body.appendChild(comp.$el)
    comp.open({
        type: 'phone',
        success: () => {
            location.href = replaceQueryString('code', '')
            window.location.reload()
        }
    })
}
复制代码

因为之前在编写绑定相关逻辑时有先见之明,提前封装了绑定的组件,所以这里可以很方便的进行调用,当服务器返回该用户没有绑定手机的信息时,则实例化绑定组件并将dom渲染到页面(这种做法是UI组件库常用的逻辑)绑定成功后,删除url中的code字段,并且刷新页面,为什么要刷新页面呢?看如下代码:

//main.js

const bootstrap = function() {
    new Vue({
        router,
        store,
        render: h => h(App)
    }).$mount('#app')
}

if (isWX() && getQueryString(window.location.href, 'code')) {
    wxLogin(bootstrap)
} else {
    bootstrap()
}
复制代码

在工程的入口页面对于根vue组件的实例化之前进行了一次判断:

如果是微信浏览器,并且url中附带了code信息,那么进入微信登录的逻辑,否则进行页面渲染

如果登录成功且已经绑定过手机,执行回调函数也就是实例化根vue:

else {
    location.href = replaceQueryString('code', '')
    callback()
}
复制代码

其实这里有点递归的意思,需要各位自行理解,不做详细说明。

既然入口文件只有在获取到code的前提下才进行微信登录,那么如果当前url没有code并且需要使用微信登录该怎么办?解决方案如下:

repInterceptor: [function(response) {
    try {
        if (response.data.success) {
            return response.data.data
        } else if ([401, 403].includes(response.data.error_code)) {
            if (timer) {
                clearTimeout(timer)
            }
            timer = setTimeout(() => {
                loginCheck(response.data.error_text)
            }, 500)
        } 
        return Promise.reject(response.data)
    } catch (e) {
        return Promise.reject(e)
    }
}, function(e) {
    // Do something with response error
    return Promise.reject({ error_text: '网络错误: ' + e })
}]
复制代码

如上是此工程的网络拦截器,如果返回401或者403说明用户在未登录的前提下请求了限制接口,那么会执行 loginCheck,而在loginCheck函数中有这样一段逻辑:

if (isWX()) {
    wxLogin()
}
复制代码

一切又回到了宇宙的中心 wxLogin ==~

相关文章:

  • 朝鲜APT集团Lazarus通过KEYMARBLE Backdoor瞄准俄罗斯组织
  • Less 日常用法
  • 手机端车牌号码键盘的vue组件
  • 回归生活:清理微信公众号
  • Cisco Nexus 系列交换机NX-OS升级
  • React开发实战
  • 工作中总结前端开发流程--vue项目
  • Java各种IO流的总结
  • MySQL-事务管理(基础)
  • Vultr 教程目录
  • Navicat
  • HTML-表单
  • mahout的数据文件格式
  • 微信小程序开发总结
  • 编码占用字节数
  • 2017-09-12 前端日报
  • 30秒的PHP代码片段(1)数组 - Array
  • CSS相对定位
  • java第三方包学习之lombok
  • JWT究竟是什么呢?
  • k个最大的数及变种小结
  • mysql innodb 索引使用指南
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • 对超线程几个不同角度的解释
  • 观察者模式实现非直接耦合
  • 解决iview多表头动态更改列元素发生的错误
  • 免费小说阅读小程序
  • 使用权重正则化较少模型过拟合
  • 异常机制详解
  • 源码安装memcached和php memcache扩展
  • 《码出高效》学习笔记与书中错误记录
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • 移动端高清、多屏适配方案
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)VC++中ondraw在什么时候调用的
  • (自适应手机端)响应式服装服饰外贸企业网站模板
  • .net core Redis 使用有序集合实现延迟队列
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .net6 webapi log4net完整配置使用流程
  • .NET值类型变量“活”在哪?
  • /etc/fstab 只读无法修改的解决办法
  • @Autowired和@Resource装配
  • @Transient注解
  • [.NET 即时通信SignalR] 认识SignalR (一)
  • [BZOJ3757] 苹果树
  • [GN] DP学习笔记板子
  • [IE编程] 如何设置IE8的WebBrowser控件(MSHTML) 的渲染模式
  • [iphone-cocos2d]关于Loading的若干处理和讨论
  • [Java][Liferay] File system in liferay
  • [LeetCode][LCR190]加密运算——全加器的实现
  • [mysql][sql]安装完mysql8跨主机不能访问解决办法