思路
通过已登录小程序去扫web端的一个二维码获取二维码的信息,然后根据这个信息请求服务是否有效。web端监听这个信息的状态,如果这个状态授权那就进入首页。
实现
1、 web端登录二维码
web端显示的二维码直接使用antd中QRCode组件。当刷新二维码时,请求服务接口拿到一个唯一code,并且存储在redis,设置时效5分钟左右;
r.GET("/qr", func(ctx *gin.Context) {
singcode := ctx.Query("singcode")
if singcode != "" {
b, vsingcode := datasource.GetRedisByString(singcode)
if b == false {
ctx.JSON(http.StatusOK, gin.H{"code": 404, "singcode": ""})
} else {
if vsingcode == "1" {
ctx.JSON(http.StatusOK, gin.H{"code": 201, "singcode": ""})
} else if vsingcode != "0" {
// 确定登录 立即删除
datasource.DelRedisByString(singcode)
ctx.JSON(http.StatusOK, gin.H{"code": 202, "singcode": vsingcode})
}
}
} else {
uuid := uuid.New()
key := uuid.String()
datasource.SetRedisByString(key, "0", 5*time.Minute)
ctx.JSON(http.StatusOK, gin.H{"code": 200, "singcode": key})
}
})
2、微信小程序扫码请求服务校验singcode是否有效,并做登录处理
微信小程序侧:
wx.scanCode({
onlyFromCamera: true,
async success (res) {
console.log(res)
const { result } = res
if (result) {
const [err,] = await checkScanCode(result)
if (!err) {
wx.showModal({
content: '是否登录?',
success (res) {
if (res.confirm) {
console.log('用户点击确定')
scanCode(result)
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
} else {
wx.showToast({
title: '确认信息失败',
icon: 'none',
duration: 2000
})
}
}
}
})
校验singcode有效性直接去访问redis是否存在key即可:
auth.GET("/checkscancode", func(ctx *gin.Context) {
singcode := ctx.Query("singcode")
b, lcode := datasource.GetRedisByString(singcode)
if singcode == "" || b == false || (b == true && lcode != "0") {
ctx.JSON(http.StatusBadRequest, "singcode"+lcode)
return
}
datasource.SetRedisByString(singcode, "1", 1*time.Minute)
ctx.JSON(http.StatusOK, gin.H{"code": 200})
})
小程序校验有效之后,就弹窗提示登录,确定登录后,请求服务将singcode状态修改,并且将小程序登录账号的id存储下来(通知web端是哪个账号登录)
auth.GET("/scancode", func(ctx *gin.Context) {
singcode := ctx.Query("singcode")
b, lcode := datasource.GetRedisByString(singcode)
if singcode == "" || b == false || (b == true && lcode != "1") {
ctx.JSON(http.StatusBadRequest, "singcode"+lcode)
return
}
tokenString := ctx.GetHeader("authorization")
if tokenString == "" {
ctx.JSON(http.StatusBadRequest, "TOKEN")
return
}
id := uitls.PaserToken(strings.Split(tokenString, "Bearer ")[1], authMiddleware)
if id == "" {
ctx.JSON(http.StatusBadRequest, tokenString+"==ID=="+strings.Split(tokenString, "Bearer ")[1])
return
}
info, st := dao.GetUserInfoByPbOpenId("", "", id, "")
if st == -1 || st == 0 {
ctx.JSON(http.StatusBadRequest, "-1")
return
}
uuid := uuid.New()
key := uuid.String()
datasource.SetRedisByString(key, info.Id, 5*time.Minute)
datasource.SetRedisByString(singcode, key, 5*time.Minute)
ctx.JSON(http.StatusOK, key)
})
3、 web端轮询检测singcode状态
登录的二维码不能长期有效,因此需要去每隔2秒去检查是否要刷新二维码。当小程序扫码确定登录之后,此时返回状态code == 202,这个时候会拿到一个 singcode,这个singcode绑定了扫码账号的用户信息,携带它去请求登录接口,就实现一个扫码登录了。
web端:
const refreshQrCode = (isout) => {
getScanCode(isout).then( res => {
const { code, singcode } = res.data
if (code === 200) {
setLoginQrCode(singcode)
} else if (code === 201) {
setQrCodeStatus("actived")
} else if (code === 404) {
setQrCodeStatus('expired')
timer && clearInterval(timer), timer = null;
return
} else if (code === 202) {
timer && clearInterval(timer), timer = null;
setQrCodeStatus("loading")
codeSin = singcode
handleLogin()
return
}
!timer && (timer = setInterval(() => {
refreshQrCode(singcode)
}, 2 * 1000))
}).catch(()=> {
setQrCodeStatus('expired')
})
}