gpt4 book ai didi

一文了解Gin对Cookie的支持

转载 作者:我是一只小鸟 更新时间:2023-08-23 14:32:02 26 8
gpt4 key购买 nike

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 信息给服务端,下面是一个大概的校验流程

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.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 对象中值,大概流程如下

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支持的介绍,希望对你有所帮助.

最后此篇关于一文了解Gin对Cookie的支持的文章就讲到这里了,如果你想了解更多关于一文了解Gin对Cookie的支持的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 8 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com