生成二维码api
- 当用户扫描生成好的二维码时,微信会根据推送事件到服务端(这个需要自己在后台配置),并且会携带好之前的scene值,后台就可以判断是哪个网页端扫了这个二维码,后台进行登录操作,
- 网页端可以通过拿着scene值轮训后台的接口,查询是否登录成功 或者利用websocket,后台主动通知你给网页,是否登录成功。
生成二维码文档地址:https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
这种方式还是对个人开发者要求太高了,个人就不能申请服务号,认证也要一笔费用,也劝退我了,pass!
小程序条件:已经上线的小程序(个人/公司) 流程:
- 后台调用微信接口https://api.weixin.qq.com/cgi-bin/token获取access_token,access_token只有两个小时,因为有调用频率限制,access_token最好是保存在缓存中;
- 用户在网页端点击获取小程序码,前端携带随机数请求后台,后台利用access_token获取小程序码https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=(其中需要携带一个参数scene需要注意,scene值就是一个随机数,需要在前端就生成好,用来后台区分网页端用),返回小程序码二进制数据;
- 前端接收后台返回的二进制数据,(此时前端需要不停地轮训后台接口查询是否登录成功,或者利用websocket,后台主动通知前端)展示小程序码,用户拿出手机用微信扫码之后,类似下面截图,
登录截图
- 用户主动点击登录按钮,登录按钮需要绑定WxLogin事件,获取用户的微信code,再调用后台提供的登录接口进行登录操作,当登陆成功完毕,通知前端完成登录
获取小程序码官方文档 https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getqrcode.html
最后,整个过程全程免费,对于我来说也是比较简单的,我先说明下,我之前是开发了一个小程序的,所以对我来说是比较方便的,如果大家没有已经上线的小程序,为了一个扫码登录,确实有点费力
原理原理图原理图
开发步骤这里我就不细讲了,知道了思路,其实开发就是最简单的事了,这里贴一些关键的代码
网页端 // 获取二维码
getQrcode = () => {
// 创建websocket连接,为了后台能主动通知到前端
if (this.State.ws == null) {
this.setState({
ws: this.createWebsocket(this)
});
}
// 发送请求 获取二维码
this.setState({
qrcodeBase64: ''
});
// 携带uuid到后台,后台将uuid和socket对象关联,方便通知到哪个网页端
getUnlimitedQrCode({ "uuid": this.state.uuid }).then((res) => {
if (res == undefined || res.data == undefined || res.data.data == undefined) {
console.log("获取二维码失败");
return null;
}
this.setState({
qrcodeBase64: res.data.data
});
}).catch((error) => {
console.error(error);
});
}
后台
// 获取不限制的小程序码接口
func (h *handler) GetUnlimitedQRCode() core.HandlerFunc {
return func(c core.Context) {
param := new(RequestQrCodeParam)
c.ShouldBindJSON(¶m)
// 获取accessToekn
accesstokenInfo, Err := h.getAccessToken()
if err != nil {
c.AbortWithError(core.error(
http.StatusBadRequest,
500,
"获取微信access_token失败").WithError(err),
)
return
}
qrCodeBytes := postUnlimitedQRCode(accesstokenInfo, param.Uuid)
// c.ResponseWriter().Write(qrCodeBytes)
var q = QrcodeRes{Data: qrCodeBytes, Errcode: 0, Errmsg: "success"}
c.Payload(q)
}
}
// websocket连接回调函数
var (
err error
server socket.Server
SocketManager = make(map[string]*websocket.Conn) // 存储uuid和socket对象的关系
)
func (h *handler) Connect() core.HandlerFunc {
return func(ctx core.Context) {
server, err = socket.New(h.logger, h.db, h.cache, ctx.ResponseWriter(), ctx.Request(), nil)
if err != nil {
return
}
// 保存uuid到map,后续后台主动回调前台,需要根据uuid找到对应的websocket连接
uuid := ctx.Request().URL.Query().Get("uuid")
log.Println(uuid)
conn, err := server.GetConn()
if err != nil {
return
}
SocketManager[uuid] = conn
go server.OnMessage()
}
}
// 登录接口
func (h *handler) Wx_web_login() core.HandlerFunc {
return func(c core.Context) {
param := new(RequestWebLoginParam)
c.ShouldBindJSON(¶m)
// 登录操作
ticketRes, err := registerAndExportToken(h, c, &WxLoginParams{Code: param.Code})
if err != nil {
c.AbortWithError(core.Error(
400,
500,
"微信登陆失败").WithError(err),
)
}
log.Print(ticketRes)
// 登陆成功后,主动调用websocket的接口,返回token
res := new(webmessage.WebSocketResponse).Success(webmessage.LOGIN_SUCCESS_TYPE, ticketRes)
// 根据uuid找到对应socket
socket := webmessage.SocketManager[param.Uuid]
socket.WriteJSON(res)
c.Payload(res)
}
}
小程序端
// 登录按钮绑定的登录事件
handleLogin = () => {
// 处理登录逻辑
console.log('登录按钮被点击')
// 获取code
Taro.login({
success: (res) => {
console.log(res)
if (res.code != '') {
// 请求登录操作
webWxLogin({ "code": res.code, "uuid": this.state.scene }).then((res) => {
console.log(res.data)
if (res.data.code == 200 && res.data.type == 20000) {
// 登录成功
Taro.showToast({
title: '登录成功',
icon: 'success'
})
// 跳转首页
Taro.switchTab({
url: '/pages/home/index'
})
} else {
Taro.showToast({
title: '登录失败',
icon: 'none'
})
}
}).catch((err) => {
console.log(err)
Taro.showToast({
title: '登录失败',
icon: 'none'
})
})
} else {
Taro.showToast({
title: '登录失败',
icon: 'none'
})
}
},
fail: (err) => {
console.log(err)
Taro.showToast({
title: '登录失败',
icon: 'none'
})
}
})
}
// 这段代码是用GitHub Copilot 写的,真的是把所有情况都写出来了
总结
这次扫码登录前前后后花了我五天的时间(下班后弄得),其中也碰了不少坑,前端二维码不显示、后台不返回二进制数据、nginx代理的https域名网站websocket也要做特殊处理,真的是在里面爬了很久,虽然这个手段不是很正规,但是过程能学到一些知识,实现了自己想要的效果,还是挺满意的,谢谢看到这里,如果哪里有说的有问题,欢迎指正