本篇会讲讲token-go扫码登录的封装和实现,给库/框架增加新的功能,最后说明使用方法,源码:https://github.com/weloe/token-go
本篇为用go设计开发一个自己的轻量级登录库/框架吧 - 秋玻 - 博客园 (cnblogs.com)的扫码登录业务篇,会讲讲扫码登录的实现,给库/框架增加新的功能,最后说明使用方法
Github:https://github.com/weloe/token-go
首先我们需要知道扫码登录流程
- 打开登录页面,展示一个二维码,同时轮询二维码状态(web)
- 打开APP扫描该二维码后,APP显示确认、取消按钮(app)
- 登录页面展示被扫描的用户头像等信息(web)
- 用户在APP上点击确认登录(app)
- 登录页面从轮询二维码状态得知用户已确认登录,并获取到登录凭证(web)
- 页面登录成功,并进入主应用程序页面(web)
我们可以知道登录的二维码有一下几种状态:
- 等待扫码
- 已扫码,等待用户确认
- 已扫码,用户同意授权
- 已扫码,用户取消授权
- 已过期
而我们扫码的客户端(一般是手机App)可以修改二维码的状态,
- 确认已扫码
- 同意授权
- 取消授权
我们封装的主要是二维码的状态维护,不包括生成二维码,二维码的生成交由使用者来实现。
而二维码的状态的常用的几个方法如下。
QRCodeId用于我们作为二维码状态的唯一标识。
在创建二维码时我们要传入QRCodeId以及timeout来设定二维码的超时时间,毕竟二维码总不能永久使用。
确认已扫码当然前提是在登录状态才能确认,因此我们用loginId作为参数用来跟QRCodeId来绑定。
对于同意授权和取消授权我们使用确认扫码的api返回的临时Token去进行操作。
而对信息的存储和获取则是使用框架内部的Adapter去获取。
首先我们要先设定一下二维码状态
等待扫码——1
已扫码,等待用户确认——2
已扫码,用户同意授权——3
已扫码,用户取消授权——4
已过期——5
维护二维码需要的信息,也就是二维码的唯一id,二维码当前状态,二维码对于的用户唯一id
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L229
在APP扫码前我们要先创建一个二维码状态,设置为WaitScan,也就是1。而创建二维码信息,也就是使用我们框架内部的Adapter接口来存储
e.spliceQRCodeKey是对存储的key的拼接方法。
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L319
通过QRCodeId使用我们的Adapter去获取
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L301
使用Adapter获取
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L311
同样使用Adapter获取
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L323
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L234
确认扫码要先判断二维码是否存在,接着校验二维码的状态是否是等待扫描WaitScan也就是1。校验完之后绑定用户唯一loginId,最后创建一个value值为QRCodeId的临时token返回。这个临时token用于同意授权和取消授权。
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L257
同意授权要使用我们在确认扫码的时候返回的临时token,首先我们要校验这个临时token,这个ParseTempToken方法就是校验临时token,获取token对应的值的接口。在校验token后获取到QRCodeId,再去校验QRCodeId对应的状态,应该要是WaitAuth等待授权,也就是2。最后就是修改二维码状态为ConfirmAuth也就3。当然不能忘记删除临时token。
https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L280
取消授权也要使用我们在确认扫码的时候返回的临时token,首先我们要校验这个临时token,这个ParseTempToken方法就是校验临时token的方法,通过这个方法获取到token对应的QRCodeId值。在校验token后获取到QRCodeId,再去校验QRCodeId对应的状态,应该要是WaitAuth等待授权,也就是2。最后就是修改二维码状态为CancelAuth也就4。同样不能忘记删除临时token。
https://github.com/weloe/token-go/blob/master/examples/qrcode/qrcode-server.go
安装token-go, go get github.com/weloe/token-go
package main
import (
"fmt"
tokenGo "github.com/weloe/token-go"
"github.com/weloe/token-go/model"
"log"
"net/http"
)
var enforcer *tokenGo.Enforcer
func main() {
var err error
adapter := tokenGo.NewDefaultAdapter()
enforcer, err = tokenGo.NewEnforcer(adapter)
enforcer.EnableLog()
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/qrcode/create", create)
http.HandleFunc("/qrcode/scanned", scanned)
http.HandleFunc("/qrcode/confirmAuth", confirmAuth)
http.HandleFunc("/qrcode/cancelAuth", cancelAuth)
http.HandleFunc("/qrcode/getState", getState)
log.Fatal(http.ListenAndServe(":8081", nil))
}
func create(w http.ResponseWriter, request *http.Request) {
QRCodeId := "generatedQRCodeId"
err := enforcer.CreateQRCodeState(QRCodeId, 50000)
if err != nil {
fmt.Fprintf(w, "CreateQRCodeState() failed: %v", err)
return
}
fmt.Fprintf(w, "QRCodeId = %v", QRCodeId)
}
func scanned(w http.ResponseWriter, req *http.Request) {
loginId, err := enforcer.GetLoginId(tokenGo.NewHttpContext(req, w))
if err != nil {
fmt.Fprintf(w, "GetLoginId() failed: %v", err)
return
}
QRCodeId := req.URL.Query().Get("QRCodeId")
tempToken, err := enforcer.Scanned(QRCodeId, loginId)
if err != nil {
fmt.Fprintf(w, "Scanned() failed: %v", err)
return
}
fmt.Fprintf(w, "tempToken = %v", tempToken)
}
func getState(w http.ResponseWriter, req *http.Request) {
QRCodeId := req.URL.Query().Get("QRCodeId")
state := enforcer.GetQRCodeState(QRCodeId)
if state == model.ConfirmAuth {
qrCode := enforcer.GetQRCode(QRCodeId)
if qrCode == nil {
fmt.Fprintf(w, "login error. state = %v, code is nil", state)
return
}
loginId := qrCode.LoginId
token, err := enforcer.LoginById(loginId)
if err != nil {
fmt.Fprintf(w, "Login error: %s\n", err)
}
fmt.Fprintf(w, "%v login success. state = %v, token = %v", loginId, state, token)
return
} else if state == model.CancelAuth {
fmt.Fprintf(w, "QRCodeId be cancelled: %v", QRCodeId)
return
}
fmt.Fprintf(w, "state = %v", state)
}
func cancelAuth(w http.ResponseWriter, req *http.Request) {
tempToken := req.URL.Query().Get("tempToken")
err := enforcer.CancelAuth(tempToken)
if err != nil {
fmt.Fprintf(w, "CancelAuth() failed: %v", err)
return
}
fmt.Fprint(w, "ConfirmAuth() success")
}
func confirmAuth(w http.ResponseWriter, req *http.Request) {
tempToken := req.URL.Query().Get("tempToken")
err := enforcer.ConfirmAuth(tempToken)
if err != nil {
fmt.Fprintf(w, "ConfirmAuth() failed: %v", err)
return
}
fmt.Fprint(w, "ConfirmAuth() success")
}
从最开始的流程和测试方法中也可以知道
首先我们需要在Web端(需要扫码登录的客户端)生成二维码后携带参数二维码id请求后端/qrcode/create
,后端调用生成二维码的方法(需要自己实现),然后调用enforcer.CreateQRCodeState()
方法初始化二维码状态。
从APP端扫码二维码,请求后端/qrcode/scanned
,后端先校验一下APP传来的token判断(使用框架的enforcer.isLoginByToken()
方法来判断)是否在登录态,使用enforcer.GetLoginId()
获取对应的loginId,再调用enforcer.Scanned()
方法。之后返回临时token。
APP端收到临时token后,选择同意或者取消授权,也就是传临时token到后端/qrcode/confirmAuth
或者/qrcode/cancelAuth
,后端调用enforcer.ConfirmAuth()
或者enforcer.CancelAuth()
方法同意或者取消授权。
而Web端在初始化二维码状态后要持续请求后端/qrcode/getState
,后端调用GetQRCodeState
方法去获取二维码状态,如果二维码状态为超时也就是Expired前端就删除二维码信息,提示二维码过期,重新生成二维码,如果获取到状态等于确认授权ConfirmAuth就进行登录操作enforcer.LoginById()
,返回登录凭证token。
__EOF__
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 我与微信审核的“相爱相杀”看个人小程序副业