用go封装和实现扫码登录

这篇具有很好参考价值的文章主要介绍了用go封装和实现扫码登录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

用go封装和实现扫码登录

本篇为用go设计开发一个自己的轻量级登录库/框架吧 - 秋玻 - 博客园 (cnblogs.com)的扫码登录业务篇,会讲讲扫码登录的实现,给库/框架增加新的功能,最后说明使用方法

Github:https://github.com/weloe/token-go

扫码登录流程

首先我们需要知道扫码登录流程

  1. 打开登录页面,展示一个二维码,同时轮询二维码状态(web)
  2. 打开APP扫描该二维码后,APP显示确认、取消按钮(app)
  3. 登录页面展示被扫描的用户头像等信息(web)
  4. 用户在APP上点击确认登录(app)
  5. 登录页面从轮询二维码状态得知用户已确认登录,并获取到登录凭证(web)
  6. 页面登录成功,并进入主应用程序页面(web)

我们可以知道登录的二维码有一下几种状态:

  1. 等待扫码
  2. 已扫码,等待用户确认
  3. 已扫码,用户同意授权
  4. 已扫码,用户取消授权
  5. 已过期

而我们扫码的客户端(一般是手机App)可以修改二维码的状态,

  1. 确认已扫码
  2. 同意授权
  3. 取消授权

实现思路

我们封装的主要是二维码的状态维护,不包括生成二维码,二维码的生成交由使用者来实现。

而二维码的状态的常用的几个方法如下。

// QRCode api
// 初始化二维码状态
CreateQRCodeState(QRCodeId string, timeout int64) error
// 获取二维码剩余时间
GetQRCodeTimeout(QRCodeId string) int64
// 获取二维码信息
GetQRCode(QRCodeId string) *model.QRCode
// 获取二维码状态
GetQRCodeState(QRCodeId string) model.QRCodeState
// 确认已扫码
Scanned(QRCodeId string, loginId string) (string, error)
// 同意授权
ConfirmAuth(QRCodeTempToken string) error
// 取消授权
CancelAuth(QRCodeTempToken string) error

QRCodeId用于我们作为二维码状态的唯一标识。

在创建二维码时我们要传入QRCodeId以及timeout来设定二维码的超时时间,毕竟二维码总不能永久使用。

确认已扫码当然前提是在登录状态才能确认,因此我们用loginId作为参数用来跟QRCodeId来绑定。

对于同意授权和取消授权我们使用确认扫码的api返回的临时Token去进行操作。

而对信息的存储和获取则是使用框架内部的Adapter去获取。

代码实现

二维码状态和信息

首先我们要先设定一下二维码状态

等待扫码——1

已扫码,等待用户确认——2

已扫码,用户同意授权——3

已扫码,用户取消授权——4

已过期——5

package model

type QRCodeState int

// QRCode State
const (
	WaitScan    QRCodeState = 1
	WaitAuth    QRCodeState = 2
	ConfirmAuth QRCodeState = 3
	CancelAuth  QRCodeState = 4
	Expired     QRCodeState = 5
)

维护二维码需要的信息,也就是二维码的唯一id,二维码当前状态,二维码对于的用户唯一id


type QRCode struct {
	id      string
	State   QRCodeState
	LoginId string
}

func NewQRCode(id string) *QRCode {
	return &QRCode{id: id, State: WaitScan}
}

初始化二维码状态

https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L229

在APP扫码前我们要先创建一个二维码状态,设置为WaitScan,也就是1。而创建二维码信息,也就是使用我们框架内部的Adapter接口来存储

func (e *Enforcer) CreateQRCodeState(QRCodeId string, timeout int64) error {
	return e.createQRCode(QRCodeId, timeout)
}
func (e *Enforcer) createQRCode(id string, timeout int64) error {
	return e.adapter.Set(e.spliceQRCodeKey(id), model.NewQRCode(id), timeout)
}

e.spliceQRCodeKey是对存储的key的拼接方法。

获取二维码的剩余时间

https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L319

通过QRCodeId使用我们的Adapter去获取

func (e *Enforcer) GetQRCodeTimeout(QRCodeId string) int64 {
    return e.getQRCodeTimeout(QRCodeId)
}
func (e *Enforcer) getQRCodeTimeout(id string) int64 {
	return e.adapter.GetTimeout(e.spliceQRCodeKey(id))
}

获取二维码信息

https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L301

使用Adapter获取

func (e *Enforcer) GetQRCode(QRCodeId string) *model.QRCode {
	return e.getQRCode(QRCodeId)
}

获取二维码状态

https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L311

同样使用Adapter获取

// GetQRCodeState
//	WaitScan   = 1
//	WaitAuth   = 2
//	ConfirmAuth  = 3
//	CancelAuth = 4
//	Expired    = 5
func (e *Enforcer) GetQRCodeState(QRCodeId string) model.QRCodeState {
	qrCode := e.getQRCode(QRCodeId)
	if qrCode == nil {
		return model.Expired
	}
	return qrCode.State
}

删除二维码信息

https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L323

func (e *Enforcer) DeleteQRCode(QRCodeId string) error {
	return e.deleteQRCode(QRCodeId)
}
func (e *Enforcer) deleteQRCode(id string) error {
	return e.adapter.Delete(e.spliceQRCodeKey(id))
}

确认扫码

https://github.com/weloe/token-go/blob/b85a297b4eae1ee730059be277d7aa83658c1fe4/enforcer_manager_api.go#L234

确认扫码要先判断二维码是否存在,接着校验二维码的状态是否是等待扫描WaitScan也就是1。校验完之后绑定用户唯一loginId,最后创建一个value值为QRCodeId的临时token返回。这个临时token用于同意授权和取消授权。

// Scanned update state to constant.WaitAuth, return tempToken
func (e *Enforcer) Scanned(QRCodeId string, loginId string) (string, error) {
	qrCode := e.getQRCode(QRCodeId)
	if qrCode == nil {
		return "", fmt.Errorf("QRCode doesn't exist")
	}
	if qrCode.State != model.WaitScan {
		return "", fmt.Errorf("QRCode state error: unexpected state value %v, want is %v", qrCode.State, model.WaitScan)
	}
	qrCode.State = model.WaitAuth
	qrCode.LoginId = loginId

	err := e.updateQRCode(QRCodeId, qrCode)
	if err != nil {
		return "", err
	}
	tempToken, err := e.CreateTempToken(e.config.TokenStyle, "qrCode", QRCodeId, e.config.Timeout)
	if err != nil {
		return "", err
	}
	return tempToken, nil
}

同意授权

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。

// ConfirmAuth update state to constant.ConfirmAuth
func (e *Enforcer) ConfirmAuth(tempToken string) error {
	qrCodeId := e.ParseTempToken("qrCode", tempToken)
	if qrCodeId == "" {
		return fmt.Errorf("confirm failed, tempToken error: %v", tempToken)
	}
	qrCode, err := e.getAndCheckQRCodeState(qrCodeId, model.WaitAuth)
	if err != nil {
		return err
	}

	qrCode.State = model.ConfirmAuth
	err = e.updateQRCode(qrCodeId, qrCode)
	if err != nil {
		return err
	}
	err = e.DeleteTempToken("qrCode", tempToken)
	if err != nil {
		return err
	}
	return err
}

取消授权

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。

// CancelAuth update state to constant.CancelAuth
func (e *Enforcer) CancelAuth(tempToken string) error {
	qrCodeId := e.ParseTempToken("qrCode", tempToken)
	if qrCodeId == "" {
		return fmt.Errorf("confirm failed, tempToken error: %v", tempToken)
	}
	qrCode, err := e.getAndCheckQRCodeState(qrCodeId, model.WaitAuth)
	if err != nil {
		return err
	}
	qrCode.State = model.CancelAuth
	err = e.updateQRCode(qrCodeId, qrCode)
	if err != nil {
		return err
	}
	err = e.DeleteTempToken("qrCode", tempToken)
	if err != nil {
		return err
	}
	return err
}

测试

func TestEnforcer_ConfirmQRCode(t *testing.T) {
	enforcer, _ := NewTestEnforcer(t)
	// in APP
	loginId := "1"
	token, err := enforcer.LoginById(loginId)
	if err != nil {
		t.Fatalf("Login failed: %v", err)
	}
	t.Logf("login token: %v", token)

	qrCodeId := "q1"

	err = enforcer.CreateQRCodeState(qrCodeId, -1)
	if err != nil {
		t.Fatalf("CreateQRCodeState() failed: %v", err)
	}
	t.Logf("After CreateQRCodeState(), current QRCode state: %v", enforcer.GetQRCodeState(qrCodeId))
	loginIdByToken, err := enforcer.GetLoginIdByToken(token)
	if err != nil {
		t.Fatalf("GetLoginIdByToken() failed: %v", err)
	}
	tempToken, err := enforcer.Scanned(qrCodeId, loginIdByToken)
	if err != nil {
		t.Fatalf("Scanned() failed: %v", err)
	}
	if state := enforcer.GetQRCodeState(qrCodeId); state != model.WaitAuth {
		t.Fatalf("After Scanned(), QRCode should be %v", model.WaitAuth)
	}
	t.Logf("After Scanned(), current QRCode state: %v", enforcer.GetQRCodeState(qrCodeId))
	t.Logf("tempToken: %v", tempToken)
	err = enforcer.ConfirmAuth(tempToken)
	if err != nil {
		t.Fatalf("ConfirmAuth() failed: %v", err)
	}
	if state := enforcer.GetQRCodeState(qrCodeId); state != model.ConfirmAuth {
		t.Fatalf("After ConfirmAuth(), QRCode should be %v", model.ConfirmAuth)
	}
	t.Logf("After ConfirmAuth(), current QRCode state: %v", enforcer.GetQRCodeState(qrCodeId))
	if enforcer.GetQRCodeState(qrCodeId) == model.ConfirmAuth {
		loginId := enforcer.getQRCode(qrCodeId).LoginId
		t.Logf("id: [%v] QRCode login successfully.", loginId)
	}
}

如何使用

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
	// use default adapter
	adapter := tokenGo.NewDefaultAdapter()
	enforcer, err = tokenGo.NewEnforcer(adapter)
	// enable logger
	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) {
	// you should implement generate QR code method, returns QRCodeId to CreateQRCodeState
	// called generate QR code, returns QRCodeId to CreateQRCodeState
	//
	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。文章来源地址https://www.toymoban.com/news/detail-711041.html

到了这里,关于用go封装和实现扫码登录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记录--vue3问题:如何实现微信扫码授权登录?

    一、需求 微信扫码授权,如果允许授权,则登录成功,跳转到首页。 二、问题 1、微信扫码授权有几种实现方式? 2、说一下这几种实现方式的原理是什么? 3、vue中的微信扫码授权登录,与uniapp和原生小程序的微信授权登录,它们之间有共同点吗? TWO 解决问题,答案速览

    2024年02月13日
    浏览(37)
  • 微信小程序实现的网页扫码授权登录(完全免费)

    微信小程序实现的网页扫码授权登录,无论是个人小程序还是企业小程序,都可以调用wx.login接口获取到openid实现微信鉴权快速扫码登录! 现如今,扫码登录已经在网站普遍应用,其中微信扫码登录极其普遍。但是微信扫码登录的实现方法有多种,大多数都是具有一些门槛的

    2024年02月05日
    浏览(32)
  • springboot整合微信(公众号)实现扫码登录(两种方式,两种实现)

    首先说一下这个微信扫码登录它的方式有两种,一种是基于网页的redirect实现,一种是基于公众号推送消息实现, 二者实现的效果是不一样的 贴一个官方文档 需要有自己的域名 (这里你可以使用内网穿透,会生成一个自己的域名,网上一大堆,自己奥利给吧) 需要申请微信认

    2024年01月18日
    浏览(82)
  • 微信公众号二维码扫码登录(SpringBoot Java实现)

    用户扫描公众号的二维码,实现登录当前平台。 若未关注公众号,则关注后触发登录;若已关注,则直接登录。  登录时通过union_id判断用户是否在系统注册,若未注册则跳转到注册页面或提示未注册。 注意,此功能使用的接口,需要 公众号类型为 服务号 才支持! 开发阶

    2024年02月12日
    浏览(39)
  • Spring Authorization Server入门 (二十) 实现二维码扫码登录

    打开网页,发起授权申请/未登录被重定向到登录页面 选择二维码登录,页面从后端请求二维码 页面渲染二维码图片,并轮询请求,获取二维码的状态 事先登录过APP的手机扫描二维码,然后APP请求服务器端的API接口,把用户认证信息传递到服务器中 后端收到APP的请求后更改

    2024年02月21日
    浏览(31)
  • 【h5+微信小程序】vue2实现h5扫码登录功能

    需要实现在同域名的h5页面上增加一个微信扫码登录的功能,如果用户已经有小程序的账号,可以直接登录。 使用 :vue2+微信小程序原生开发 可以实现上述功能的 前提 是:同一用户,对同一个微信开放平台下的不同应用,UnionID是相同的。域名已经配置。 可以用什么来区分

    2024年02月14日
    浏览(48)
  • Java 实现微信扫码登录方法(提供前端及后端核心代码)

    思路 1、Vue前端页面获取一个公众号的二维码,不是普通二维号,是带有场景值的 2、java后端接收前端的请求,生成一个带时效性的二维码链接返回给前端 3、公众号平台配置服务器接口地址 4、接收到关注或扫码请求并相应处理 5、前端轮询状态,如果检查到验证通过进到下

    2024年02月19日
    浏览(36)
  • 手把手教程用Java实现微信公众号扫码登录功能

    微信现今是我们必不可少的社交工具了,围绕微信这个生态实际上有很多东西可以做,我们经常会看到一些网站通过微信扫码进如公众号登录。一方面方便了用户登录,另外一方面也可以给公众号带来一定的流量,今天就通过 Java SpringBoot 项目来用微信公众号参数二维码实现

    2024年02月06日
    浏览(50)
  • vue3 微信扫码登录及获取个人信息实现的三种方法

    一、流程: 微信提供的扫码方式有两种,分别是: 根据文档我们可以知道关于扫码授权的模式整体流程为: 二、前置条件: 微信开发官网 申请: appid: ‘’, // 后端提供 redirect_uri: ‘’, // 后端提供 AppSecret // 后端提供 三、具体登录实现 实现方式一: 使用vue插件: 使用: 结果

    2023年04月13日
    浏览(36)
  • 前端实现 微信扫码登录网站 pc端(二维码嵌套页面) 超详细,包会

    本人申明:本案例使用到的appid和AppSecret都是无效的 appid:应用唯一标识,在微信开放平台提交应用审核通过后获得 AppSecret:在微信开放平台提交应用审核通过后获得 1.在根目录html文件引入,既index.html 2.通过js添加节点 注意事项: 如果二维码出来,但是跳转失败,一定要看

    2024年02月04日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包