一文了解Gin对Cookie的支持
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
信息,获取请求用户信息。
当前常用的有Cookie
,Jwt
,OAuth2.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=/
在这个例子中,服务器设置了一个名为username
的Cookie
,它的值是abc
,过期时间是2023年6月9日,路径为/
。浏览器在接收到该Cookie
时,便将其保存起来。
客户端请求时携带Cookie
的方式,则是通过HTTP协议中的Cookie
头部字段,客户端可以通过该头部字段携带信息给服务器端,比如下面这个例子:
Cookie: sessionid=1234
在这个例子中,HTTP请求中携带了一个name
为sessionid
,value
为 1234
的 Cookie
。当服务器端接收到该HTTP
请求后,从中解析出Cookie
的信息,然后基于此实现后续的流程。
3.2 实现说明
回看上述流程,主要分为两个大部分: 客户端和服务器端。在客户端部分,关键任务包括保存浏览器返回的Cookie
信息以及在请求时携带Cookie
信息给服务器。对于服务器端,则是在通过身份校验之后,能够按照规范客户端返回Cookie
,并在接收到请求时,能够正确解析出请求中的 Cookie
信息,识别出用户信息。
对于客户端部分,在浏览器接收到HTTP响应时,如果响应体中有Set-Cookie
头部字段,浏览器会自动保存Cookie
信息;客户端发起请求时,需要将 Cookie
信息传递给服务器。此时浏览器会自动携带通过校验的Cookie
。如果通过校验,此时会在HTTP请求头中携带Cookie
信息给服务端,下面是一个大概的校验流程:
在整个流程中,客户端保存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.Context
中Cookie
方法获取到HTTP请求头部中Cookie
的信息 ,然后验证Cookie
中的value是否满足预期。
这个是一个简单的代码示例,比如用户身份认证机制等,则需要自行完善,这里不再完整展示。
4. 原理
下面将简单描述gin.Context
对象中SetCookie
方法和Cookie
方法的实现原理,帮助读者更好使用这两个API
。
4.1 SetCookie方法
SetCookie
方法的实现原理如下,首先,SetCookie
方法会创建一个http.Cookie
对象,并设置其名称、值、路径、域名、过期时间等属性。例如,以下代码创建了一个名为sessionid
的Cookie
:
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
对象中创建一个新的值列表;否则,会在已有的值列表末尾添加新的值,大概流程如下:
在返回HTTP响应时,会遍历Header
对象,填充HTTP响应头部信息,然后返回给客户端,比如上面Header
生成的HTTP响应头部如下:
Set-Cookie: v1
Set-Cookie: v2
Agent: Windows
SetCookie
方法便是通过上述所说流程,将Cookie
的信息设置到HTTP响应体头部当中去,然后返回给客户端。
4.2 Cookie方法
在调用 Cookie()
方法时,系统会首先检查请求头部中是否包含名为 Cookie
的字段。如果该字段不存在,则返回空字符串。
如果请求头部中包含 Cookie
字段,同时Cookie
的name
为调用Cookie()
方法指定的值,则系统会解析该字段并将其转换为一个 http.Cookie
对象。这个对象包含了所有的 Cookie
属性,例如名称、值、路径、过期时间、域名等等。最后,返回转换后的http.Cookie
对象中值,大概流程如下:
总的来说,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支持的介绍,希望对你有所帮助。
一文了解Gin对Cookie的支持的更多相关文章
- 一文彻底弄懂cookie、session、token
前言 作为一个JAVA开发,之前有好几次出去面试,面试官都问我,JAVAWeb掌握的怎么样,我当时就不知道怎么回答,Web,日常开发中用的是什么?今天我们来说说JAVAWeb最应该掌握的三个内容. 发 ...
- 1.部分(苹果)移动端的cookie不支持中文字符,2.从json字符串变为json对象时,只支持对象数组
1.移动端的cookie不支持中文字符.可以用编码,解码的方式解决. 2.json字符串变成相应 的,json对象数组字符串.就这样 3.不同客户端(移动端.电脑)的请求,在C#服务端的取时间的格式竟 ...
- cookie不支持中文,必须转码后存储,否则会乱码
cookie不支持中文,必须转码后存储,否则会乱码 Cookie ck = new Cookie("username", URLEncoder.encode(name, " ...
- 文档资源搜索小工具 - 支持PDF,DOC,PPT,XLS
最近做了一个文档搜索小工具,当然不是网盘搜索工具,这个工具支持四种文件格式搜索(pdf,doc,ppt,xls),你只需要在搜索框中输入你想要搜索资源的关键词,点击搜索按钮即可获取相关资源,点击下载按 ...
- MFC单文档程序添加HTML帮助支持
1.在App类 构造函数中添加 EnableHtmlHelp(); 2.在Frame类中,添加消息影射: ON_COMMAND(ID_HELP_FINDER, CFrameWnd::OnHelpFin ...
- 为WebClient增加Cookie的支持
我们经常会在应用程序中使用到WebClient模拟访问网站资源并且进行处理,如果多次访问之间我们希望为他们保存Cookie,换句话说,第一个请求产生的Cookie能自动带到第二个请求的话,可以通过自定 ...
- 【抱怨文】vscode对多项目支持不够友好
vscode是一个简单的开发工具,启动快,速度快.但是当前1.26版本对多项目支持好像有点问题.命令行有个dotnet sln,但是只能添加新项目,却没有创建解决方案的命令.如果强行添加他会提示没有解 ...
- 一文彻底搞懂Cookie、Session、Token到底是什么
> 笔者文笔功力尚浅,如有不妥,请慷慨指出,必定感激不尽 Cookie 洛:大爷,楼上322住的是马冬梅家吧? 大爷:马都什么? 夏洛:马冬梅. 大爷:什么都没啊? 夏洛:马冬梅啊. 大爷:马什 ...
- 【ASP.NET 进阶】仿百度文库文档在线预览(支持格式.pdf,.doc,docx,xls,xlsx,.ppt,pptx)
在[ASP.NET]PDF文件在线预览(类似百度文库)基础上进行了office文件到pdf文件的转换,然后在显示出来,效果如下: 问题说明: 1.请通过以下方式添加 Office COM 组件. 2. ...
- 检测浏览器是否支持cookie方法
cookie 摘自: http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.html Cookie是什么? Cookie 是一小段文本信息 ...
随机推荐
- vue中嵌入MP4 只有声音没图像
最近一个项目需要在页面嵌入一段视频,当然首选iframe了,直接嵌入了youku的视频,没问题,我想ok了.于是将url替换为本地的MP4发现只有声音没有任何图片,奇怪了,我首先想到是不是vue项目使 ...
- MySQL的sql语句执行流程(简述)
导言: MySQL和服务器端对接的时候,我们知道一般就是服务器端会打包一些SQL命令去增删改查数据库,这个打包的数据库SQL语句数据包一般为4MB,再大一些就不会被数据库端接收了 但是我们可以自己更改 ...
- RabbitMQ 工作模式介绍
RabbitMQ 工作模式介绍 1.Hello World RabbitMQ 是一个消息代理:它接受并转发消息.您可以将其视为邮局:当您将要邮寄的邮件放入邮箱时,您可以确定信使最终会将邮件交付给您的收 ...
- 【Clickhouse】ReplaceingMergeTree引擎final实现合并去重探索
前言 在OLAP实践中,在有数据更新的场景中,比如存储订单数据,我们经常会用到ReplaceingMergeTree引擎来去重数据,以获取数据的最新状态.但是ReplaceingMergeTree引擎 ...
- Kubernetes(k8s)网络策略NetworkPolicy
目录 一.系统环境 二.前言 三.网络策略(NetworkPolicy)简介 四.创建pod和svc 五.没有网络策略的条件下访问pod 六.给pod添加网络策略 6.1 入站网络策略 6.1.1 入 ...
- 【AGC】云监控日志服务查询不到Logger日志相关问题2
[关键字] AGC.云监控.日志服务 [问题描述] 之前有开发者反馈在使用AGC云监控,填写了Logger日志,但是在云监控的日志服务查不到的问题.具体如下所述: 云函数按要求写了Logger日志, ...
- 尚医通day13【预约挂号】(内附源码)
页面预览 预约挂号 根据预约周期,展示可预约日期,根据有号.无号.约满等状态展示不同颜色,以示区分 可预约最后一个日期为即将放号日期 选择一个日期展示当天可预约列表 预约确认 第01章-预约挂号 接口 ...
- 前端学习C语言 - 初级指针
初级指针 本篇主要介绍:指针和变量的关系.指针类型.指针的运算符.空指针和野指针.指针和数组.指针和字符串.const 和指针.以及gdb 调试段错误. 基础概念 指针是一种特殊的变量.存放地址的变量 ...
- React ISR 如何实现 - 最后的 Demo
之前写了两个 demo 讲解了如何实现 SSR 和 SSG,今天再写个 demo 说在 ISR 如何实现. 什么是 ISR ISR 即 Incremental Static Regeneration ...
- MySQL 存储引擎 InnoDB 内存结构之更改缓冲区
更改缓冲区(Change Buffer)是一种特殊的数据结构,用于缓存不在缓冲池中的二级索引(secondary index)页的更改.可能来自于INSERT.UPDATE或DELETE操作(数据操作 ...