一文瞭解Gin對Cookie的支持z

来源:https://www.cnblogs.com/chenjiazhan/archive/2023/08/23/17650046.html
-Advertisement-
Play Games

類載入器 虛擬機設計團隊把類載入階段中的“通過一個類的全限定名來獲取描述此類的二進位位元組流”這個動作放到Java虛擬機外部去實現,以便讓應用程式自己決定如何去獲取所需要的類。實現這個動作的代碼模塊稱為“類載入器”。 類載入器可以說是Java語言的一項創新,也是Java語言流行的重要原因之一,它最初是 ...


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支持的介紹,希望對你有所幫助。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ##### 15 JavaScript ES6中的箭頭函數 1. 什麼是箭頭函數 ES6中允許使用=>來定義函數。箭頭函數相當於匿名函數,並簡化了函數定義。 2. 基本語法 ```js // 箭頭函數 let fn = (name) => { // 函數體 return `Hello ${name} ...
  • 13 JavaScript關於prototype(超重點) prototype是js裡面給類增加功能擴展的一種模式. 寫個面向對象來看看. ```js function People(name, age){ this.name = name; this.age = age; this.run = f ...
  • 本文,我們將一起學習,使用純 CSS,實現如下所示的動畫效果: ![](https://img2023.cnblogs.com/blog/608782/202308/608782-20230822102547750-742841232.gif) 上面的動畫效果,非常有意思,核心有兩點: 1. 小球隨 ...
  • ##### 12 eval函數 eval本身在js裡面正常情況下使用的並不多. 但是很多網站會利用eval的特性來完成反爬操作. 我們來看看eval是個什麼鬼? 從功能上講, eval非常簡單. 它和python裡面的eval是一樣的. 它可以動態的把字元串當成js代碼進行運行. ```js s = ...
  • 在 Infinispan 配置文件中切換髮現協議從廣播到組播,需要修改 JGroups 的配置,因為 Infinispan 使用 JGroups 來處理集群通信和發現。下麵是一個示例,展示如何將配置從廣播切換到組播。 首先,確保您已經有一個 Infinispan 配置文件,比如 `infinispa ...
  • Lora晶元的PCB板受力接收信號有問題可能有電路板設計問題、電路板受潮或受損、外部干擾、設備相容性問題等原因及其解決辦法... ...
  • 小程式平臺是怎麼保證商家業務的穩定、健康發展,服務好這些外部商家的呢?這裡面非常重要的是我們平臺對小程式基本流量的運營與監控。如何不讓業務的小程式線上上裸奔?如何幫助業務對自身小程式流量的沖高回落有一種直觀的把握和監測?如何基於海量數據指導業務去進行一個精細化的運營?實際上,京東小程式數據中心就扮演... ...
  • 本文介紹的 MetaQ/RocketMQ 是側重於維持消息一致性和高可靠性的消息隊列中間件,幫助大家對隊列設計的理解。 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...