gin框架39--重构 BasicAuth 中间件

这篇具有很好参考价值的文章主要介绍了gin框架39--重构 BasicAuth 中间件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

每当我们打开一个网址的时候,会自动弹出一个认证界面,要求我们输入用户名和密码,这种BasicAuth是最基础、最常见的认证方式,gin框架中提供了一种内置的方式,但它只能用内置的用户和密码,无法使用外部db中的用户和密码,这种方式很多时候是不友好的。
为此,本文根据gin.BasicAuth的原理对其就行重构,实现一个简单的newAuth中间件,该中间件可以代替默认的BasicAuth,并且可以按需更改为自定义查询函数,实现从外部db或者用户管理系统查询信息实现登录认证的功能。

gin BasicAuth 解析

博文 gin框架14–使用 BasicAuth 中间件 介绍了BasicAuth 中间件的基础使用方法,直接使用 gin.BasicAuth(gin.Accounts{“foo”: “bar”, “austin”: “1234”, “lena”: “hello2”, “manu”: “4321”, }) 即可,非常简单实用。

实际上当我们访问url的时候,它会从请求的 Authorization 中获取用户信息,并和gin.Accounts中内置用户对比,如果用户存在就将用户名称存放在Context的 Keys map结构中,方便后续查找或者获取用户信息;如果不存在就设置c.Header(“WWW-Authenticate”, realm), 并返回c.AbortWithStatus(http.StatusUnauthorized),浏览器上的表现就是重新弹出输入用户名和密码的窗口 。

核心逻辑在 BasicAuthForRealm 方法中,如下所示:

func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
	if realm == "" {
		realm = "Authorization Required"
	}
	realm = "Basic realm=" + strconv.Quote(realm)
	pairs := processAccounts(accounts)
	return func(c *Context) {
		// Search user in the slice of allowed credentials
		user, found := pairs.searchCredential(c.requestHeader("Authorization"))
		if !found {
			// Credentials doesn't match, we return 401 and abort handlers chain.
			c.Header("WWW-Authenticate", realm)
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		// The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
		// c.MustGet(gin.AuthUserKey).
		c.Set(AuthUserKey, user)
	}
}

自定义newAuth实现基础认证

gin.BasicAuth 只能提供默认的认证功能,且需要内置指定的用户|密码,但实际在代码中hardcode大量用户信息是不科学的,因此我们需要自己重构一个BasicAuth来实验基础认证功能。
此处实现了一个newAuth中间件,该中间件会判断用户是否输入账号|密码,并通过judgeUserExist来判断账号|密码是否正确,正确则返回用户信息,不正确则返回http.StatusUnauthorized, 具体案例如下。

此处为了简洁方便,此处直接内置了3个用户到users中,并用 judgeUserExist 查询用户账号密码是否正确。实际项目中可将该方法更改为查询db,无需在项目中hardcode内置用户。

package main

import (
	"encoding/base64"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"strings"
)

var users = gin.H{
	"foo":    gin.H{"email": "foo@bar.com", "phone": "123433", "pwd": "bar"},
	"austin": gin.H{"email": "austin@example.com", "phone": "666", "pwd": "123"},
	"lena":   gin.H{"email": "lena@guapa.com", "phone": "523443", "pwd": "456"},
}

func help() string {
	helpStr := `hello gin:
127.0.0.1:8088/your-api
/auth/user
`
	return helpStr
}

func judgeUserExist(userName, userPwd string) (string, bool) {
	// 实际项目中将该函数更改为从db查询即可,此处为了简单直接从预定的users中查询。
	msg := ""
	tag := false
	if userInfo, ok := users[userName]; ok {
		pwd, ok := userInfo.(gin.H)["pwd"]
		if ok && pwd == userPwd {
			msg = fmt.Sprintf("用户%v密码正确", userName)
			tag = true
		} else {
			msg = fmt.Sprintf("用户%v密码不正确", userName)
		}
	} else {
		msg = fmt.Sprintf("用户%v不存在", userName)
	}
	return msg, tag
}

func getUserPwdFromAuthorization(auth string) (user, pwd string) {
	// auth[:6]="Basic "
	base64UserPwd, err := base64.StdEncoding.DecodeString(auth[6:])
	if err != nil {
		panic(err)
	}
	base64UserPwdStr := string(base64UserPwd)
	colonIndex := strings.Index(base64UserPwdStr, ":")
	user = base64UserPwdStr[:colonIndex]
	pwd = base64UserPwdStr[colonIndex+1:]
	return user, pwd
}

func newAuth(realm string) func(c *gin.Context) {
	if realm == "" {
		realm = "Authorization Required"
	}
	realm = "Basic realm=" + strconv.Quote(realm)
	return func(c *gin.Context) {
		authHeader := c.Request.Header.Get("Authorization") // 获取请求头中的数据
		if authHeader == "" {
			c.Header("WWW-Authenticate", realm)
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		} else {
			user, pwd := getUserPwdFromAuthorization(authHeader)
			// fmt.Printf("user=%v,pwd=%v\n", user, pwd)
			msg, tag := judgeUserExist(user, pwd)
			if !tag {
				// c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"msg": msg, "tag": tag})
				fmt.Println(msg)
				c.AbortWithStatus(http.StatusUnauthorized)
				return
			}
			c.Set(gin.AuthUserKey, user)
		}
	}
}

func userHandler(c *gin.Context) {
	user := c.MustGet(gin.AuthUserKey).(string)
	c.IndentedJSON(http.StatusOK, gin.H{
		"status":   200,
		"msg":      "it's fine",
		"userInfo": users[user],
	})
}

func main() {
	app := gin.Default()
	app.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, help())
	})

	authorized := app.Group("/auth", newAuth(""))
	authorized.GET("/user", userHandler)
	app.Run(":8088")
}

输出:
gin框架39--重构 BasicAuth 中间件,Golang,gin,重构,中间件文章来源地址https://www.toymoban.com/news/detail-721457.html

注意事项

  1. c.Header中需要添加 WWW-Authenticate 字段,否则初次访问的时候不会弹出输入用户名、密码的框!!!

说明

  1. 测试环境
    ubuntu22.04 Desktop
    go1.20.7
  2. 参考文档
    using-basicauth-middleware
    Gin框架 -- 中间件

到了这里,关于gin框架39--重构 BasicAuth 中间件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • gin中间件篇

    所有请求都经过此中间件 输出结果: 输出结果: 效果演示: 定义程序计时中间件,然后定义2个路由,执行函数后应该打印统计的执行时间,如下: 效果演示: 参考文章: https://www.fansimao.com/928855.html  gin Next()方法 - 范斯猫

    2024年01月21日
    浏览(29)
  • Go Gin中间件

    Gin是一个用Go语言编写的Web框架,它提供了一种简单的方式来创建HTTP路由和处理HTTP请求。中间件是Gin框架中的一个重要概念,它可以用来处理HTTP请求和响应,或者在处理请求之前和之后执行一些操作。 以下是关于Gin中间件开发的一些基本信息: 中间件的定义 :在Gin中,中

    2024年02月05日
    浏览(37)
  • GO——gin中间件和路由

    中间件 参考:https://learnku.com/articles/66234 结构 中间件是函数 中间件函数被放在调用链上 调用链的末尾是路由path对应的函数 执行过程 net/http包调用到gin的serverHTTP 参考:go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:506 通过path找到路由对应的处理链,赋值给context 参考:go/pkg/mod/git

    2024年01月17日
    浏览(33)
  • gin中使用限流中间件

    限流又称为流量控制(流控),通常是指限制到达系统的并发请求数,本文列举了常见的限流策略,并以gin框架为例演示了如何为项目添加限流组件。 限流又称为流量控制(流控),通常是指限制到达系统的并发请求数。 我们生活中也会经常遇到限流的场景,比如:某景区

    2024年01月25日
    浏览(35)
  • gin 中间件流程控制:Next()、 Abort()

    源码注释: Next 应该只在中间件内部使用。它执行调用处理程序内部链中的挂起处理程序。 通俗的说,就是中间件放行,当一个中间件代码执行到 Next() ,会先执行它之后的函数,最后再来执行完本函数。 eg: 如果其中一个中间件响应了c.Abort(),后续中间件将不再执行,直接按

    2024年02月15日
    浏览(48)
  • go gin 全局中间件,以及设置值

    2024年02月11日
    浏览(29)
  • 简单记录下gin中使用中间件记录操作日志

    1、直接定义中间件 2、在需要使用的地方直接使用就可以,自动会收集日志到数据库中

    2024年02月09日
    浏览(32)
  • gin自定义中间件解决requestBody不可重复读问题

    先直接上代码 注意,上面的中间件,需要在第一个执行。 在gin中,context.Request.Body 是一个io.ReadCloser的接口,如下图 查看io.ReadCloser接口定义 我们发现io.ReaderCloser接口的本质就是 Read(p []byte) (n int, err error) 和 Close() error 的组合。 所以我们只需要自己编写实现 Read(p []byte) (n in

    2024年02月01日
    浏览(72)
  • Gin CORS 跨域请求资源共享与中间件

    1.1 什么是浏览器的同源策略? 同源策略 (Same origin policy) 是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现 浏览器最基本的安

    2024年01月25日
    浏览(37)
  • Go学习第十七章——Gin中间件与路由

    Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等 即比如,如果访问一个网页的话,不管访问什么路径都需要进行登录,

    2024年02月07日
    浏览(35)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包