1. 二维码的本质
二维码就是根据某种约定的编码方式将一段文本信息转换为一个能够被解码识别的图片。其本质就是一段文本信息。 既然是文本信息,可能是如下一些信息用途类别: 人的姓名/一个数字/一段文字/一个网址/一个验证码/一个订单ID/会员ID/优惠券编号/位置坐标/WiFi密码......
常见的“安卓分享wifi密码”,我家狗狗的识别牌上是不知道啥鬼东西的数字;扫描后是一个网址等;
那么关键点就是编码方式,到底什么是编码方式? 这里就不缀叙了;可自行谷歌
怎么生成二维码
如果掌握了二维码的编码方式,是可以通过纸和笔将二维码图片画出来的,就是太麻烦了
一般是通过编写计算机程序的方式将文本信息编码为一个二维码图片。 如果普通人不会编写计算机程序,那么就只能利用别人编写好的程序了。 比方我就找的第三方;
扫描二维码登陆网站的流程
具体到实践,因为我的项目是vue文档,所以下面示例子均以vue为主;
- 下载安卓qrcodejs2
npm i qrcodejs2 -save
- 导入
import QRCode from 'qrcodejs2'
- 页面标签
<div id="qrcode"></div>
- 绘制 // new QRCode(elementId, options)
const qrcode = new QRCode("qrcode", {
text: "测试 随便网址也行 文字也行",
width: 128,
height: 128,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.H
});
这时二维码已经绘制出来,审查元素,可以发现其是生成了canvas;
结合实践的经验,扫码登陆时,里面的网址需要携带id,被客户端扫码解析到后才能携带设备的信息以及uid等通知服务器已经扫码,让服务器把对象的扫码用户信息告知给PC网页;
用户扫码登录后获取的token有24小时的过期时间,所以在这阶段重新,为了用户体验,打开是不用重新扫码登录的;因此需要进行缓存;
相关准备工具
// 缓存的存取删
export function cache(key, value, seconds) {
value = JSON.stringify(value)
var timestamp = Date.parse(new Date()) / 1000
if (key && value === null) {
//删除缓存
localStorage.removeItem(key);
} else if (key && value) {
//设置缓存
let expire = !seconds ? timestamp + (3600 * 24 * 7) : timestamp + seconds
value = value + "|" + expire
localStorage.setItem(key, value);
} else if (key) {
//获取缓存
var val = localStorage.getItem(key);
if (!val) {
return false;
}
var tmp = val.split("|")
if (!tmp[1] || timestamp >= tmp[1]) {
localStorage.removeItem(key)
return false
} else {
return JSON.parse(tmp[0])
}
} else {
console.error("key不能空")
}
}
export const ERR_CODE = {
EXPIRED: 515, // 二维码code已经过期
UNAUTHORIZED: 516 // 还未授权(未扫描授权)
}
// token过期时间
export const TOKEN_EXPIRED_SECONDS = 86400 // 1 * 24 * 60 * 60
export const CATCH_KEY = 'PC_LOGIN_INFO'
export const STEP = {
NEED_LOGIN: 1, // 需要登录
CAN_UPLOAD: 2, // 能够上传
UPLOAD_SUCCESS: 3 // 上传成功
}
// 清除二维码
export function clearQrCode(qrcodeInstance, deepClear = false) {
qrcodeInstance && qrcodeInstance.clear();
// 解决扫描成功后,页面依旧有二维码img存在的问题
if (deepClear) {
const imgs = [...document.getElementsByTagName('img')]
imgs.forEach(img => {
if (img.alt === 'Scan me!') {
img.style.display = 'none'
return
}
})
}
}
逻辑
是否已经登录并且缓存信息未过期
methods: {
judgeTokenIsValid() {
const useInfo = cache(CATCH_KEY)
if (useInfo) {
Object.assign(this.params, useInfo)
this.pageStep = STEP.CAN_UPLOAD
} else {
this.$nextTick(() => {
// 实例化QRCode
qrcodeInstance = new QRCode('qrcode', {
width: 212,
height: 212
})
// 获取二维码的id
this.getCodeId()
})
Object.assign(this.params, { token: ''})
this.pageStep = STEP.NEED_LOGIN
}
return Boolean(useInfo)
},
// 获取二维码id并绘制二维码
async getCodeId() {
const { qr_code } = await $http(API.GET_CODE_ID, {}, "POST")
if(qrcodeInstance) {
clearQrCode(qrcodeInstance, false)
// 二维码信息由客户端指定给出
qrcodeInstance.makeCode(`http://xxx?type=slogin&id=${qr_code}`)
}
this.qrcodeExpired = false
// 轮询二维码是否已经被扫描授权
this.getQrLoginInfo(qr_code)
},
// 轮询二维码是否已经被扫描授权
getQrLoginInfo(qr_code) {
timeout && clearTimeout(timeout)
timeout = setTimeout(async () => {
try {
const params = await $http(API.GET_LOGIN_INFO, { qr_code })
// 已经扫描后,缓存用户信息,处理页面展示
Object.assign(this.params, params)
this.pageStep = STEP.CAN_UPLOAD
cache(CATCH_KEY, params, TOKEN_EXPIRED_SECONDS)
this.clearQrCode(true)
} catch ({ code }) {
// 处理返回结果
this.handleCodeErr(code, qr_code)
}
}, 3000)
},
handleCodeErr(code, qr_code) {
switch (code) {
case ERR_CODE.EXPIRED:
this.qrcodeExpired = true
break;
case ERR_CODE.UNAUTHORIZED:
this.getQrLoginInfo(qr_code)
break;
}
},
}