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

news2024/12/23 9:15:01

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

  • 介绍
  • gin BasicAuth 解析
  • 自定义newAuth实现基础认证
  • 注意事项
  • 说明

介绍

每当我们打开一个网址的时候,会自动弹出一个认证界面,要求我们输入用户名和密码,这种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")
}

输出:
在这里插入图片描述

注意事项

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

说明

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1113743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Godot 官方2D C#重构(2):弹幕躲避

前言 Godot 官方 教程 Godot 2d 官方案例C#重构 专栏 Godot 2d 重构 github地址 实现效果 技术点说明 异步函数 Godot的事件不能在Task中运行,因为会导致跨线程的问题。 //这样是不行的,因为跨线程了,而且会阻塞UI线程,具体原因…

高效恢复丢失的文件的10 款Android数据恢复工具

在当今快节奏的数字时代,从Android设备丢失重要数据可能是一场噩梦。 您需要一个可靠的恢复工具来取回您的数据,例如令人难忘的照片,重要的联系人,重要的工作文档等。 值得庆幸的是,有许多高效的Android数据恢复工具可…

2023年中国人力资源咨询发展历程及市场规模前景分析[图]

人力资源咨询是企业借助外部智力资源提高自身管理水平和效率的重要路径,属于管理咨询业的一个重要分支, 一方面,人力资源咨询要为企业提供基础的人力资源外包服务;另一方面,人力资源咨询要为企业提供专业化、职业化现代人力资源管…

《深入浅出OCR》实战:基于DBNet的文字检测

✨专栏介绍: 经过几个月的精心筹备,本作者推出全新系列《深入浅出OCR》专栏,对标最全OCR教程,具体章节如导图所示,将分别从OCR技术发展、方向、概念、算法、论文、数据集等各种角度展开详细介绍。 💙个人主页: GoAI |💚 公众号: GoAI的学习小屋 | 💛交流群: 7049325…

【LeetCode】54. 螺旋矩阵

1 问题 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5] 示例 2: 输入:matri…

「Qt中文教程指南」如何创建基于Qt Widget的应用程序(三)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。 本文描述了如何使用…

多态的使用以及多态底层的实现(上)

什么是多态 我们让不同的对象去完成同一件事情,这件事情的结果是不一样的,例如买火车票,我们学生买火车票,普通人买火车票,或是军人买火车票最后结果都是不一样的。 多态的要求是什么 首先一定是要在继承中 虚函数…

机器学习笔记 - 深度学习中跳跃连接的直观解释

一、概述 如今人们利用深度学习做无数的应用。然而,为了理解在许多作品中看到的大量设计选择(例如跳过连接),了解一点反向传播机制至关重要。 如果你在 2014 年尝试训练神经网络,你肯定会观察到所谓的梯度消失问题。简单来说:你在屏幕后面检查网络的训练过程,你看到的只…

AI驱动的未来:探索人工智能的无限潜力 | 开源专题 No.39

这一系列开源项目代表着多个领域的最新技术成果,包括深度学习、自然语言处理、计算机视觉和分布式训练。它们共同的特点是致力于教育、资源分享、开源精神、多领域应用以及性能和效率的追求,为广大开发者、研究者和学生提供了宝贵的工具和知识&#xff0…

layui框架实战案例(21):layui table单元格显示图片导致复选框冗余的解决方案

图片自适应表格CSS 为防止单元格内的图片不能正常显示,需本地重写CSS。 /*layui-table图片自适应*/ .layui-table-cell {height: auto;line-height: 20px;}.layui-table-cell img {height: 50%;max-width: 50%; }列代码 , cols: [[{type: checkbox,fixed:left, w…

高精度时间测量(TDC)电路MS1022

MS1022 是一款高精度时间测量电路,内部集成了模拟比 较器、模拟开关、施密特触发器等器件,从而大大简化了外 围电路。同时内部增加了第一波检测功能,使抗干扰能力大 大提高。通过读取第一个回波脉冲的相对宽度,用户可以获 得接…

图像信号处理板设计原理图:2-基于6U VPX的双TMS320C6678+Xilinx FPGA K7 XC7K420T的图像信号处理板

综合图像处理硬件平台包括图像信号处理板2块,视频处理板1块,主控板1块,电源板1块,VPX背板1块。 一、板卡概述 图像信号处理板包括2片TI 多核DSP处理器-TMS320C6678,1片Xilinx FPGA XC7K420T-1FFG1156,1片X…

【ROS 2 Humble】 Ubuntu 20.04(Focal)平台 源码安装

提问链接: https://answers.ros.org/questions/ —————————————— ROS 1 各版本支持截止时间查看 ROS 1 支持时间查看 链接 https://wiki.ros.org/Distributions ROS 2 各版本支持截止时间查看 ROS 2 版本支持时间查看 ROS 2 https://docs.ros.…

开赛在即 | 赛宁网安技术支撑第七届“蓝帽杯”决赛

为全面贯彻网络强国重要思想,进一步加强落实网络安全人才战略部署,推动网络安全技术交流发展,促进高校尖端人才培养,提升专职人员安全技能。由公安部网络安全保卫局、教育部教育管理信息中心、中国教育协会指导,中国人…

vue3 列表页开发【选择展示列】功能

目录 背景描述: 开发流程: 详细开发流程: 总结: 背景描述: 这个功能是基于之前写的 封装列表页 的功能继续写的,加了一个选择展示列的功能,可以随时控制表格里展示那些列的数据&#xf…

榜样力量激发青少年英语学习新活力

近日,在第二届iEnglish英语风采秀总决选中,12岁的天津女孩田丽雨以出色的英语能力和生动表演赢得了年度舞台之星的称号。田丽雨的英语学习方法引发了社会各界的广泛关注,尤其是她以身边榜样为动力,借助智能英语学习工具iEnglish实现了英语自由阅读和无障碍交流的目标。 据中国青…

[数据挖掘、数据分析] clickhouse在go语言里的实践

系列文章目录 [数据挖掘] clickhouse在go语言里的实践 [数据挖掘] 用户画像平台构建与业务实践 文章目录 系列文章目录前言一、clickhouse的起源二、OLAP/OLTP2.1、主流的OLAP/OLTP数据库 三、go语言开发实践3.1、安装配置go语言环境,配置IDE3.1.1、Go开发环境安装…

Spring框架漏洞学习之第二篇(CVE-2022-22965、CVE-2022-22947、CVE-2018-1273、CVE-2018-1270)

写在前边 如果在浏览本文时发现技术性错误时,欢迎指正;如果需要本框架探测的POC和EXP也可以私信,我看到后也会及时恢复大家。 总之,欢迎各路大佬叨扰! Spring Framework 远程代码执行漏洞(CVE-2022-22965&…

vue3中computed的用法

一、完整代码 <template><div class"about"><h1>Computed的用法</h1><h3>姓:{{ person.firstName }}</h3><input type"text" v-model"person.firstName"><h3>名:{{ person.lastName }}</h3…