一文了解Gin对Cookie的支持z

news2025/1/13 13:08:09

1. 引言

本文将从Web应用程序处理请求时需要用户信息,同时HTTP又是无状态协议这个矛盾点出发。从该问题出发,简单描述了解决该问题的Token 机制,进而引出Cookie的实现方案。

基于此我们将详细描述Cookie的规范,然后详细描述具体的实现方式,进一步描述Gin 框架对Cookie 操作提供的API,最终提供了一个详细的代码实现。

我们还将详细描述Gin 框架提供API 的实现原理,帮助用户更好得使用这两个API

2. 问题引入

在 如何使用Gin搭建一个Go Web应用程序 一文中,我们已经了解了如何使用Gin 搭建一个简单的Web应用程序。然而,在现实的Web应用程序中,大部分功能都是需要用户的身份信息才能处理。举例来说,在一个视频网站查看用户最近观看记录,如果缺少用户身份信息,此时将无法对请求进行处理。

但是HTTP协议的设计,是无状态的,也就是每次请求都是独立的。基于此,应该有一套机制,能够在用户身份认证成功后,给用户分配一个Token,后续用户在每次请求时,都携带上该Token,使得服务器能够从请求中获取用户信息,解决HTTP无状态问题。大概流程如下:

上面流程中,需要服务端按照某个协议,向客户端返回Token;客户端通过该协议,成功解析出服务端返回的Token,然后在每次请求中携带该Token。然后服务器端再根据协议,从中解析出Token 信息,获取请求用户信息。

当前常用的有CookieJwtOAuth2.0 等标准,其各有优缺点。其中Cookie 是一种存储在客户端浏览器中的数据。服务端可以通过设置HTTP响应头将Token 存储在Cookie当中,并在后续请求中从Cookie 中读取Token。而JWT 则是一种基于JSON格式的安全令牌,可用于在客户端和服务端之间传递信息。

之前,我们在 一文读懂Cookie 中,已经了解Cookie的相关内容。基于此,我们这次使用Cookie 来实现上述所说的流程,按照Cookie的规范来实现Token的返回和请求中Token 的解析。

3. 实现

3.1 Cookie规范说明

这里我们对HTTP协议中的Cookie 规范再补充一下,这里分为两部分,第一部分是服务端如何向客户端发送 Cookie ,第二部分是客户端向服务端发送请求时如何携带Cookie 信息。

对于服务端向客户端发送Cookie的手段,HTTP协议存在一个Set-Cookie 的头部字段,服务器可以通过Set-Cookie 头部字段将Cookie发送给客户端。例如下面这个例子:

Set-Cookie: username=abc; expires=Wed, 09 Jun 2023 10:18:14 GMT; path=/

  在这个例子中,服务器设置了一个名为usernameCookie,它的值是abc,过期时间是2023年6月9日,路径为/ 。浏览器在接收到该Cookie 时,便将其保存起来。

  客户端请求时携带Cookie的方式,则是通过HTTP协议中的Cookie头部字段,客户端可以通过该头部字段携带信息给服务器端,比如下面这个例子:

Cookie: sessionid=1234

在这个例子中,HTTP请求中携带了一个namesessionidvalue1234Cookie。当服务器端接收到该HTTP 请求后,从中解析出Cookie的信息,然后基于此实现后续的流程。

3.2 实现说明

回看上述流程,主要分为两个大部分: 客户端和服务器端。在客户端部分,关键任务包括保存浏览器返回的Cookie信息以及在请求时携带Cookie 信息给服务器。对于服务器端,则是在通过身份校验之后,能够按照规范客户端返回Cookie,并在接收到请求时,能够正确解析出请求中的 Cookie 信息,识别出用户信息。

对于客户端部分,在浏览器接收到HTTP响应时,如果响应体中有Set-Cookie 头部字段,浏览器会自动保存Cookie信息;客户端发起请求时,需要将 Cookie 信息传递给服务器。此时浏览器会自动携带通过校验的Cookie。如果通过校验,此时会在HTTP请求头中携带Cookie信息给服务端,下面是一个大概的校验流程:

image.png

在整个流程中,客户端保存Token信息和在请求时携带Token信息这两部分工作,浏览器已经帮我们实现了。剩下的工作集中在服务端的,主要涉及按照Cookie的规范给客户端返回用户标识,并在接收到客户端请求时从HTTP请求中读取Cookie以获取到用户的信息。与Cookie相关的详细内容可以参考文章一文读懂Cookie。

因此下面我们需要做的两件事情,其一,服务器需要按照Cookie的规范往客户端发送Cookie的内容;其次,服务器在处理请求时,需要从HTTP请求头中读取出Cookie的信息,成功识别用户身份。

Gin 框架中提供了一些API,能够帮助我们在服务端,按照Cookie规范给客户端发送Cookie 信息,同时也有API 能够帮助我们解析Cookie 的信息。下面我们先来了解相关的API,然后再基于这些API ,搭建一个能够自动识别用户信息的 Web 应用程序。

3.3 API说明

3.3.1 SetCookie

gin.Context 对象中的 SetCookie 方法用于向客户端返回响应的同时,在Set-Cookie头部携带Cookie 信息。下面是该方法的详细说明:

func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
  • name:cookie 的名称(必须)。
  • value:cookie 的值(必须)。
  • maxAge:cookie 的过期时间,以秒为单位。如果为负数,则表示会话 cookie(在浏览器关闭之后删除),如果为零,则表示立即删除 cookie(可选,默认值为-1)。
  • path:cookie 的路径。如果为空字符串,则使用当前请求的 URI 路径作为默认值(可选,默认值为空字符串)。
  • domain:cookie 的域名。如果为空字符串,则不设置域名(可选,默认值为空字符串)。
  • secure:指定是否仅通过 HTTPS 连接发送 cookie。如果为 true,则仅通过 HTTPS 连接发送 cookie;否则,使用 HTTP 或 HTTPS 连接都可以发送 cookie(可选,默认值为 false)。
  • httpOnly:指定 cookie 是否可通过 JavaScript 访问。如果为 true,则无法通过 JavaScript 访问 cookie;否则,可以通过 JavaScript 访问 cookie(可选,默认值为 true)。

在处理函数中,通过调用SetCookie 方法,便可以向客户端发送一个HTTP cookie。这里举一个代码示例,来帮助读者更好得理解该API,下面举一个代码示例,如下:

func main() {
  router := gin.Default()

  router.GET("/set-cookie", func(c *gin.Context) {
    c.SetCookie("user", "john", 3600, "/", "", false, true)
    c.String(http.StatusOK, "cookie set successfully")
  })

  router.Run(":8080")
}

在这个示例中,使用 SetCookie 方法设置一个名为user的 cookie。这个 cookie 的值是john,在 1 小时后过期。该代码还设置了路径为“/”以及HttpOnly属性为true。

下面启动该服务器,客户端向服务端发送请求,请求路径为/set-cookie,上面的处理函数将会被执行,然后我们来看其响应内容:

# 1. 发送请求
curl -i http://localhost:8080/set-cookie
# 2. 返回响应
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Set-Cookie: user=john; Path=/; Max-Age=3600; HttpOnly
Date: Sun, 20 Aug 2023 07:39:15 GMT
Content-Length: 23

cookie set successfully

查看上面第6行,可以看到,我们通过SetCookie方法,成功设置了一个Cookie,然后以在HTTP头部的形式返回。

3.1.2 Cookie方法

往客户端返回Cookie后,浏览器会将Cookie保存起来,然后在下次请求时将Cookie跟随请求一起发送给服务器端。

在HTTP无状态协议的情况下,我们使用Cookie 来识别用户信息,此时服务器端需要正确解析出HTTP 头部中Cookie的信息,Gin 框架中的gin.Context 提供了Cookie方法,方便我们获取到Cookie的信息。下面是该方法的定义说明:

func (c *Context) Cookie(name string) (string, error) 

使用Cookie方法可以获取指定名称的Cookie值,如果不存在指定名字的Cookie,此时将会返回错误。下面给一个简单示例代码的说明:

func main() {
    router := gin.Default()

    // 定义路由
    router.GET("/cookie", func(c *gin.Context) {
        // 获取名为 "username" 的 cookie
        cookie, err := c.Cookie("username")
        if err != nil {
            // 如果 cookie 不存在,则返回错误信息
            c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
            return
        }

        // 在响应中返回 cookie 值
        c.JSON(http.StatusOK, gin.H{"username": cookie})
    })

    router.Run(":8080")
}

在上述示例中,我们定义了一个 /cookie 路由,使用 c.Cookie("username") 方法来获取名为 username 的 Cookie 值。如果 Cookie 不存在,则返回一个错误响应。否则,我们将在响应中返回 Cookie 的值。

下面我们通过curl 命令来对/cookie 请求,通过 -b 标识来携带cookie 值:

# -v, --verbose 这个参数会打开curl的详细模式,输出一些额外的信息,包括HTTP请求和响应头信息。
curl -b -v -b "username=hello cookie;" http://localhost:8080/cookie

下面我们来看具体的请求体和响应体的内容:

GET /cookie HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.79.1
Accept: */*
Cookie: username=hello cookie;

可以看到,我们请求体携带了Cookie 字段,Cookie 的名称为 username,我们前面服务器端便是尝试获取名为 username 的 Cookie,下面我们看请求的响应体,看是否成功解析了HTTP 请求 Cookie的内容:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 20 Aug 2023 08:12:45 GMT
Content-Length: 27

{"username":"hello cookie"}

可以看到,服务端程序通过Cookie方法成功解析了HTTP请求头部中Cookie字段的值,然后将解析的结果正常返回客户端。

3.4 代码实现

下面我们来搭建一个基于Cookie 实现用户身份验证的Web 应用程序,首先需要一个登录页面,用于验证用户身份信息,验证通过后,我们将通过Cookie 给客户端返回一个 Token

同时,我们还需要创建一个页面,需要验证用户身份信息,在验证过程中,我们会检查用户请求中是否携带Cookie,同时Cookie 中携带的数据是否正确,基于此实现用户身份的验证。下面是一个简单代码的示例:

func main() {
   route := gin.Default()

   route.GET("/login", func(c *gin.Context) {
      // HTTP 响应中携带 Cookie
      // Set cookie {"label": "ok" }, maxAge 30 seconds.
      c.SetCookie("label", "ok", 30, "/", "localhost", false, true)
      c.String(200, "Login success!")
   })

   route.GET("/home", func(c *gin.Context) {
      // 获取 name = label 的 Cookie 的 value
      if cookie, err := c.Cookie("label"); err == nil {
         // 判断 Cookie的value 是否满足预期
         if cookie == "ok" {
            c.JSON(200, gin.H{"data": "Your home page"})
         }
      } else {
         c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden with no cookie"})
      }
   })

   route.Run(":8080")
}

首先是一个/login 请求路由,通过SetCookie 方法给客户端返回Cookie,基于此返回用户Token

然后/home 路由的处理,则是通过gin.ContextCookie 方法获取到HTTP请求头部中Cookie的信息 ,然后验证Cookie 中的value是否满足预期。

这个是一个简单的代码示例,比如用户身份认证机制等,则需要自行完善,这里不再完整展示。

4. 原理

下面将简单描述gin.Context 对象中SetCookie 方法和Cookie方法的实现原理,帮助读者更好使用这两个API

4.1 SetCookie方法

SetCookie 方法的实现原理如下,首先,SetCookie 方法会创建一个http.Cookie对象,并设置其名称、值、路径、域名、过期时间等属性。例如,以下代码创建了一个名为sessionidCookie:

cookie := &http.Cookie{
    Name:    "sessionid",
    Value:   "1234",
    Expires: time.Now().Add(24 * time.Hour),
    Path:    "/",
    Domain:  "",
    Secure:  false,
    HttpOnly:true,
}

接下来,将上述Cookie对象转换为字符串格式,并设置到HTTP响应头的Set-Cookie字段中。代码实现如下:

func SetCookie(w ResponseWriter, cookie *Cookie) {
   if v := cookie.String(); v != "" {
      w.Header().Add("Set-Cookie", v)
   }
}

这里第三行将Cookie 存储到Header 对象当中,Header 是专门用于存储HTTP响应头部的信息。调用Add 方法时,会根据指定的Key,在 Header 对象中查找相应的值列表。如果这个键不存在,则会在 Header 对象中创建一个新的值列表;否则,会在已有的值列表末尾添加新的值,大概流程如下: image.png

在返回HTTP响应时,会遍历Header 对象,填充HTTP响应头部信息,然后返回给客户端,比如上面Header 生成的HTTP响应头部如下:

Set-Cookie: v1
Set-Cookie: v2
Agent: Windows

SetCookie 方法便是通过上述所说流程,将Cookie 的信息设置到HTTP响应体头部当中去,然后返回给客户端。

4.2 Cookie方法

在调用 Cookie() 方法时,系统会首先检查请求头部中是否包含名为 Cookie的字段。如果该字段不存在,则返回空字符串。

如果请求头部中包含 Cookie 字段,同时Cookiename 为调用Cookie() 方法指定的值,则系统会解析该字段并将其转换为一个 http.Cookie 对象。这个对象包含了所有的 Cookie 属性,例如名称、值、路径、过期时间、域名等等。最后,返回转换后的http.Cookie 对象中值,大概流程如下:

image.png

总的来说,Cookie() 方法的实现原理比较简单,它只是通过查找 HTTP 请求头部中的 Cookie 信息,并将其转换为 http.Cookie 对象来获取请求中特定 Cookie 值。

5. 总结

在本文中,我们深入探讨了Web应用程序在处理用户信息时所面临的挑战,特别是在HTTP协议作为无状态协议的背景下。我们从这一矛盾出发,介绍了解决方案中的Token机制,并引出了基于Cookie的实现方案。

我们详细阐述了Cookie的规范,包括服务端如何发送Cookie以及客户端如何在请求中携带Cookie信息。

我们进一步深入探讨了具体的实现方式,并介绍了Gin框架提供的API,这些API使得在服务端按照Cookie规范发送和解析Cookie变得更加容易。通过一个实际的代码示例,我们演示了如何使用这些API来构建一个基于Cookie实现用户身份验证的Web应用程序。

在探讨API的使用之余,我们也深入剖析了Gin框架提供的API的实现原理,为读者提供了更深层次的理解。

基于此,完成了对Gin中Cookie支持的介绍,希望对你有所帮助。

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

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

相关文章

UE4如何连接dmx---摇头矩阵灯具的创建

UE4如何连接dmx---摇头矩阵灯具的创建 开始创建库! 然后我们开始创建多少个灯珠(注意了:这是矩阵灯,是看灯珠的) 那么这里我们创建6X6灯珠 下面设置灯珠的属性,灯珠有什么属性呢,只有颜色属性&…

高忆管理:沪指震荡跌0.24%,医药、酿酒等板块走低,数据要素概念逆市活跃

22日早盘,两市股指高开低走,沪指盘中冲高回落,创业板指走势疲弱;北向资金净卖出超40亿元。 到午间收盘,沪指跌0.24%报3085.48点,深成指跌0.73%,创业板指跌1.3%;两市合计成交4510亿元…

Python 自定义装饰器与上下文管理

1、定义装饰器实现计算函数运行时间 # 自定义装饰器 def compute_time(func):def compute(*args, **kwargs):st time.time()result func(*args, **kwargs)et time.time()print(消费时间 %.6f s % (et - st))return resultreturn compute# 使用 compute_time def work(work_i…

(2018,解释可视化)GAN 解剖:可视化和理解生成对抗网络

GAN dissection: Visualizing and understanding generative adversarial networks 公众号:EDPJ 目录 0. 摘要 1. 简介 2. 相关工作 3. 方法 3.1 通过解剖表征单元 3.2 使用干预措施衡量因果关系 4. 结果 4.1 跨数据集、层和模型的单位比较 4.2 诊断和改…

VMware 使用U盘进入PE系统,下划线光标闪烁

一、前言 vmware虚拟机各种原因崩溃,然后又没有快照,怎么办? 或者 密码忘记了无法开机,这时候就想到使用PE了。 二、分析 但是使用U盘进入PE的时候,遇到了各种问题: 加载U盘修改启动顺序启动后出现下划线…

DiscuzQ 二开教程(7)——二次开发版本部署文档

DiscuzQ 二开教程(7)——二次开发版本部署文档 源码:Discuz-Q-V3: 本仓库为Discuz-Q V3.0.211111 版本的二次开发版本,是将DiscuzQ官方仓库进行合并代码(All in One)整理后的仓库,使用更方便。…

【2023年11月第四版教材】《第6章-项目管理概论》(合集篇)

《第6章-项目管理概论》 1 章节内容2 项目基础3 项目经理的角色3.1 项目经理的影响力范围3.2 项目经理领导力风格 4 价值驱动的项目管理知识体系4.1 开发生命周期类型 5 五大过程组6 五个过程组和十大知识领域 1 章节内容 【本章分值预测】大部分为新增内容,预计选…

ARL资产侦察灯塔 指纹增强

项目:https://github.com/loecho-sec/ARL-Finger-ADD 下载项目后运行 python3 ARl-Finger-ADD.py https://你的vpsIP:5003/ admin password该项目中的finger.json可以自己找到其他的指纹完善,然后运行脚本添加指纹。

微信开发之一键修改群聊备注的技术实现

修改群备注 修改群名备注后,如看到群备注未更改,是手机缓存问题,可以连续点击进入其他群,在点击进入修改的群,再返回即可看到修改后的群备注名,群名称的备注仅自己可见 请求URL: http://域名地…

Python采集电商平台泳衣数据进行可视化分析

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 环境使用: python 3.8 解释器 pycharm 编辑器 模块使用: 第三方模块 需要安装 requests —> 发送 HTTP请求 内置模块 不需要安装 csv —> 数据处理中经常会用到的一种文件格式 第三方模块安装&#xff1a…

高性能服务器Nodejs快速入门

目录 1 初识 Nodejs2 Buffer 缓冲区3 fs 文件系统模块3.1 读取文件3.2 写入文件3.3 路径动态拼接问题 __dirname3.4 其它操作 4 path 路径模块4.1 路径拼接 path.join()4.2 获取路径中文件名 path.basename()4.3 获取路径中文件扩展名 path.extname() 5 http 模块5.1 创建基本 …

2023年高教社杯数学建模思路 - 复盘:人力资源安排的最优化模型

文章目录 0 赛题思路1 描述2 问题概括3 建模过程3.1 边界说明3.2 符号约定3.3 分析3.4 模型建立3.5 模型求解 4 模型评价与推广5 实现代码 建模资料 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 描述 …

Prompt本质解密及Evaluation实战(一)

一、基于evaluation的prompt使用解析 基于大模型的应用评估与传统应用程序的评估不太一样,特别是基于GPT系列或者生成式语言模型,因为模型生成的内容与传统意义上所说的内容或者标签不太一样。 以下是借用了ChatGPT官方的evaluation指南提出的对结果的具…

Http2.0协议深入解析

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…

ChatGPT、Google Bard、Claude2、新BING哪一款人工智能聊天机器人适合自己

人工智能聊天机器人正在提高数无数专业人士的工作效率。下面我们就来看看目前最流行的几款强大的人工智能工具,以及它们具体如何帮助到你。 今年7月AI圈最大的动静之一便是AI初创公司Anthropic发布了其AI聊天机器人Claude最新版本——Claude2。该聊天机器人对标Open…

AP9235 dc-dc升压恒流电源驱动IC 2000ma SOT23-6

概述 AP9235B 系列是一款固定振荡频率、恒流输出的升压型DC/DC转换器,非常适合于移动电话、PDA、数码相机等电子产品的背光驱动。输出电压可达30V ,3.2V输入电压可以驱动六个串联LED, 2.5V输入电压可以驱动两路并联LED(每路串联…

机器学习简介

文章目录 引言1. 从找规律说起2. 机器学习应用2.1 有监督学习2.2 无监督学习2.2.1 聚类2.2.2 降维 3. 机器学习一般流程4. 机器学习常用概念5. 深度学习简介5.1 引入 -- 猜数字5.2 深度学习5.2.1 隐含层/中间层5.2.2 随机初始化5.2.3 损失函数5.2.4 导数与梯度5.2.5 梯度下降5.…

【linux kernel】linux内核中的debugfs

文章目录 一、👉相关文件二、👉简介三、👉debugfs的API1、在debugfs中创建目录2、在debugfs目录中创建文件3、创建一个具有初始大小的文件4、创建包含单个整数值(十进制)的文件5、创建包含单个十六进制值得文件&#x…

AI Agent在家务场景下的AgentBench基准测试

近日,来自清华大学、俄亥俄州立大学和加州大学伯克利分校的研究者设计了一个测试工具——AgentBench,用于评估LLM在多维度开放式生成环境中的推理能力和决策能力。研究者对25个LLM进行了全面评估,包括基于API的商业模型和开源模型。 他们发现,顶级商业LLM在复杂环境中表现出…

文件内容搜索工具 - Python实现

在本篇文章中,我们将介绍如何使用 wxPython 库创建一个简单的文件搜索工具。这个工具允许用户选择一个文件夹,并在该文件夹中的所有 .py 文件中查找指定的文字,并显示匹配的位置。 C:\pythoncode\blog\searchwordinpyfile.py 代码实现 我们首…