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

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 credentialsuser, 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 mainimport ("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 := falseif 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.rhkb.cn/news/164203.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ整理

MQ(Message Queue):是队列,也是跨进程的通信机制,用于上下游传递信息 FIFO(First In First Out):先进先出 RabbitMQ访问:http://127.0.0.1:15672/ 默认账号密码:guest 优势:流量削峰&#x…

CRC16计算FC(博途SCL语言)

CRC8的计算FC,相关链接请查看下面文章链接: 博途SCL CRC8 计算FC(计算法)_博途怎么计算crc_RXXW_Dor的博客-CSDN博客关于CRC8的计算网上有很多资料和C代码,这里不在叙述,这里主要记录西门子的博途SCL完成CRC8的计算过程, CRC校验算法,说白了,就是把需要校验的数据与多项式…

CCF CSP认证 历年题目自练Day34

题目一 试题编号: 202303-1 试题名称: 田地丈量 时间限制: 1.0s 内存限制: 512.0MB 问题描述: 问题描述 西西艾弗岛上散落着 n 块田地。每块田地可视为平面直角坐标系下的一块矩形区域,由左下角坐标 (x1,…

xml schema中的all元素

说明 xml schema中的all元素表示其中的子元素可以按照任何顺序出现&#xff0c;每个元素可以出现0次或者1次。 https://www.w3.org/TR/xmlschema-1/#element-all maxOccurs的默认值是1&#xff0c;minOccurs 的默认值是1。 举例 <element name"TradePriceRequest&…

丈母娘说:有编制的不如搞编程的

10月17日百度世界大会召开&#xff0c;据说文心大模型又牛X了&#xff0c;综合水平相比GPT4毫不逊色&#xff0c;真是个让人激动的消息&#xff0c;国产大模型的进展可以说是日新月异&#xff0c;我这耳朵边一直响彻四个字&#xff1a;遥遥领先。 不过今天我关注的重点不是什么…

ArcGIS在VUE框架中的构建思想

项目快要上线了&#xff0c;出乎意料的有些空闲时间。想着就把其他公司开发的一期代码里面&#xff0c;把关于地图方面的代码给优化一下。试运行的时候&#xff0c;客户说控制台有很多飘红的报错&#xff0c;他们很在意&#xff0c;虽然很不情愿&#xff0c;但能改的就给改了吧…

云函数cron-parser解析时区问题

1、问题 云函数部署后cron-parser解析0点会变成8点 考虑可能是时区的问题 然后看文档发现果然有问题&#xff0c;云函数环境是utc0 2、解决 看了半天cron-parser文档发现 Using Date as an input can be problematic specially when using the tz option. The issue bein…

【Linux】线程池 | 自旋锁 | 读写锁

文章目录 一.Linux线程池1.线程池的概念2.线程池的优点3.线程池的应用场景4.线程池的实现 二.其他常见的锁1.STL、智能指针和线程安全2.其他常见的锁 三.读者写者问题1.读者写者模型2.读写锁 一.Linux线程池 1.线程池的概念 线程池是一种线程使用模式。 线程过多会带来调度开销…

网站二级域名怎么部署SSL证书?

二级域名是在主域名下创建的子域名&#xff0c;常用于区分不同功能或部门的网站。随着互联网的发展&#xff0c;越来越多的网站开始采用二级域名来构建更灵活和个性化的网站结构&#xff0c;保护二级域名的数据安全也变得至关重要。为了确保二级域名的安全性&#xff0c;申请SS…

【七:(测试用例)spring boot+testng+xml+mock实现用例管理+数据校验】

目录 1、目录结构的相关类cases类1、添加用户 AddUserTest2、获取用户列表信息 GetUserInfoListTest3、获取用户信息 GetUserInfoTest4、登录测试5、更新用户信息 config类1、报告配置2、用户路径配置 model类utils类 配置配置类SQLMapper.xmlspring boot全局配置databaseConfi…

了解 Elasticsearch 自动生成的文档 _id:重复是一个问题吗?

Elasticsearch 中自动生成的文档 ID 当你在未指定 ID 的情况下对文档建立索引时&#xff0c;Elasticsearch 会自动为该文档生成唯一的 ID。 该 ID 是 Base64 编码的 UUID&#xff0c;由多个部分组成&#xff0c;每个部分都有特定的用途。 ID 生成过程针对索引速度和存储效率进…

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

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

用CSS+SVG做一个优雅的环形进度条

开门见山 先上最终效果图&#xff1a;&#xff08;Demo 传送门&#xff09; 其中进度、尺寸、环宽和颜色都可以非常方便地进行控制。 核心原理&#xff1a; 利用两个重叠的圆环形&#xff0c;通过对上层圆环弧长的控制来表示进度&#xff0c;下层圆环则作为辅助&#xff0c;…

开源云财务软件,财务软件源码,永久免费财务软件

纷析云SAAS云财务软件开源版 包含账套、凭证字、科目、期初、币别、账簿、报表、凭证、结账 技术交流群 扫码添加客服进群 商业版地址 纷析云商业版https://f3.fenxi365.com/ 正式环境&#xff0c;可注册账号直接使用或测试 功能对比 功能模块开源版商业版[技术重构]凭证✔…

蜻蜓c影视追剧系统-多个小程序添加说明

多小程序添加设置 蜻蜓c影视追剧 支持多小程序添加&#xff0c;也就是可以管理不同前端的小程序。 此处id 对应前端小程序的mp值 关于添加小程序&#xff1a; 此处有所有填写内容的参考方式&#xff0c;要注意是必须开通了微信支付才可以添加&#xff0c;这里需要添加证书信息…

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

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

微信小程序文本横向无缝滚动

背景&#xff1a; 微信小程序中列表宽度不够长&#xff0c;其中某字段显示不完整&#xff0c;因此要使其自动滚动。 &#xff08;最初看网上很多用定时器实现&#xff0c;但他们的案例中都只是一个横幅、用定时器也无所谓。但是我的需求中是一个上下无限滚动的列表&#xff0c;…

离线语音与IoT结合:智能家居发展新增长点

离线语音控制和物联网&#xff08;IoT&#xff09;相结合在家居中具有广泛的应用和许多优势。离线语音控制是指在设备在本地进行语音识别和处理&#xff0c;而不需要依赖云服务器进行处理。IoT是指借助网络&#xff0c;通过手机APP、小程序远程控制家居设备。 启英泰伦基于AI语…

【面试系列】Vue

引言&#xff1a;下面是一些常见的 Vue 面试题和对应的答案 目录 1. Vue 是什么&#xff1f;它有哪些特点&#xff1f;2. Vue 的生命周期有哪些&#xff1f;请简要描述每个生命周期的作用。3. Vue 组件间通信的有哪些方式&#xff1f;4. Vue 的 computed 和 watch 的区别是什么…