Gin框架之Session的使用(詳解)

前言

本文主要介紹 go開源框架gin 是如何使用session的。以及前端 vue3 + [email protected] 是如何配合session一起使用的

主要介紹內容包括這些:

  1. session的基本原理
  2. gin配置session
  3. axios 如何設置攜帶cookie (chrome版本 < 80)
  4. axios 如何在跨域請求時攜帶cookie(chrome版本 < 80)
  5. chrome瀏覽器版本 >= 80 時,axios 不攜帶cookie的解決方案
  6. session的一些基本配置操作
  7. session的Flashes講解

正文

session的基本原理

http協議是無狀態的,就是說你在請求服務器同時,張三也在請求服務器,這時候服務器是不知道哪個請求是你,哪個請求張三(除非你們攜帶了一些識別字段,不過這屬於業務層內容,不在討論範圍,我們單說http協議)。

爲了讓服務器知道哪個請求時你的,哪個是張三的。這時候就有了session。

session和cookie是不分家的。我們每次說到session,其實默認就是要使用cookie了。

我們舉例說明一下什麼是session:

你要去逛澡堂,剛進門來到服務檯時你是沒有開儲物櫃的鑰匙的。當你交錢了,服務檯就給你一把儲物櫃的鑰匙,這時儲物櫃就和你的鑰匙綁定了。你去游泳、搓澡,你一直會把鑰匙帶在身上。這時候你不管什麼時候去服務檯,只要你拿着鑰匙,就會認識你,你拿着鑰匙可以隨便的到儲物櫃中拿取自己的東西。你要離開時,把鑰匙櫃服務檯就可以了。

在程序裏是怎麼實現上面的過程的呢?
服務器就是服務檯,儲物櫃就是session倉庫,鑰匙就是我們的憑證。攜帶鑰匙其實就是cookie傳輸。

第一次登錄,服務器給客戶端頒發一個唯一的sessionId, 並通過http的響應頭返回。客戶端(瀏覽器)發現返回的數據中有cookie數據就把這個cookie數據存放到內存。下次再發送http請求時,把內存中的cookie數據再塞到http請求頭中,一併發給服務器,服務器在解析請求時,發現請求頭中有cookie,就開始識別cookie中的sessionId,拿到sessionId,我們就知道這個請求時由哪個客戶端發送來的了。

gin配置session

gin框架在處理session時有專門的中間件,我們可以直接使用。

中間件: github.com/gin-contrib/sessions , 我們直接安裝依賴: go get github.com/gin-contrib/sessions 即可引入使用

sessions使用教程

我們參考官網案例:

package main

import (
        // 導入session包
	"github.com/gin-contrib/sessions"
       // 導入session存儲引擎
	"github.com/gin-contrib/sessions/cookie"
        // 導入gin框架包
	"github.com/gin-gonic/gin"
)

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

        // 創建基於cookie的存儲引擎,shuiche 參數是用於加密的密鑰,可以隨便填寫
	store := cookie.NewStore([]byte("shuiche"))

        // 設置session中間件,參數mysession,指的是session的名字,也是cookie的名字
       // store是前面創建的存儲引擎
	r.Use(sessions.Sessions("mysession", store))

	r.GET("/test", func(c *gin.Context) {
                // 初始化session對象
		session := sessions.Default(c)
                
                // 通過session.Get讀取session值
                // session是鍵值對格式數據,因此需要通過key查詢數據

		if session.Get("hello") != "world" {
                        // 設置session數據,()
			session.Set("hello", "world")
                        // 刪除session數據
                        session.Delete("tizi365")
                        // 保存session數據
			session.Save()
                        // 刪除整個session
                        // session.Clear()
		}
                
		c.JSON(200, gin.H{"hello": session.Get("hello")})
	})
	r.Run(":8000")
}

使用sessions 中間件注意要點:

  1. session 倉庫其實就是一個 map[interface]interface 對象,所有 session可以存儲任意數據
  2. session 使用的編解碼器是自帶的gob,所以存儲類似: struct、map 這些對象時需要先註冊對象,不然會報錯 gob: type not registered for...
  3. session 存儲引擎支持: cookie、內存、mongodb、redis、postgres、memstore、memcached 以及 gorm 支持的各類數據庫(mysql、sqlite)
  4. session 在創建時有一個配置項,可以配置session過期時間、cookie、domain、secure、path等參數
  5. 調用 session 方法: Set()、 Delete()、 Clear()、方法後,必須調用一次 Save() 方法。否則session數據不會更新

gob註冊案例

type User struct{
  Name string
}

gob.Register(User{})

session配置項案例

// store 就是前面創建的存儲引擎
store.Options(sessions.Options{
		Secure:   true,
		SameSite: 4,
		Path:     "/",
		MaxAge:   m.MaxAge,
	})

axios 如何設置攜帶cookie (chrome版本 < 80)

axios請求默認是不攜帶 Cookie 的,如果需要攜帶 Cookie 前端需要配置

// 全局配置
axios.defaults.withCredentials = true

// 實例配置
const service = axios.create({
	baseURL: process.env.BASE_API,
	timeout: 5000,
	withCredentials: true  //  配置項
})

// 單個路由配置
axios.get('/test', {
  withCredentials: true
})

axios 如何在跨域請求時攜帶cookie(chrome版本 < 80)

涉及到跨域,我們就要想到要在服務器端做處理
我們給服務器開啓 CORS 跨域,我們給gin添加 CORS 中間件

跨域配置中間件

func(c *gin.Context) {
		method := c.Request.Method
		origin := c.GetHeader("Origin")
		c.Header("Access-Control-Allow-Origin", origin) // 注意這一行,不能配置爲通配符“*”號
		c.Header("Access-Control-Allow-Credentials", "true") // 注意這一行,必須設定爲 true
		c.Header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers,Cookie, Origin, X-Requested-With, Content-Type, Accept, Authorization, Token, Timestamp, UserId") // 我們自定義的header字段都需要在這裏聲明
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type,cache-control")

		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			//c.AbortWithStatus(http.StatusNoContent)
			c.AbortWithStatus(http.StatusOK)
		}
		// 處理請求
		c.Next()
	}

chrome瀏覽器版本 >= 80 時,axios 不攜帶cookie的解決方案

如果我們的chrome瀏覽器版本高於80,我們會發現按照上面兩步配置的cookie還是無法生效,這是因爲chrome爲了增加安全性修改了攜帶cookie的方式。

這裏分兩種情況講解:

線上情況

  1. 設置: samesite: none,也就是session的store在設定options時設定的字段配置爲:SameSite: 4,(這裏4 就代表none)。
  2. secure 配置爲 true
  3. 服務端開啓https (必須)

1、2 配置案例查看 上面的 session配置項案例

開發環境

用127.0.0.1地址或者localhost地址測試

優化方案

我們可以利用cookie機制發送sessionId。目前我正在考慮使用自定義header字段攜帶sessionId,基本邏輯:
前端:請求返回後,判斷是否有我們命名的session 名稱的cookie,(就是創建session時的自定義名稱:mysession),如果有將值存儲到內存。 發送請求時,將存儲的session值添加到http請求的header中。

服務端:將校驗session的中間件中的獲取session 修改爲從header中獲取。

因爲chrome>80的瀏覽器是發不出去cookie,不是收不到cookie。我們就不用瀏覽器的字段攜帶cookie機制,搞成手動攜帶cookie

session的一些基本配置操作

設置session過期時間

設置:store.Options(sessions.Options{MaxAge: 86400*30})

session 刷新超時時間

基本思路:

每次獲取到請求後, 我們將該用戶的session重新賦值,並返回給客戶端,客戶端下次請求時攜帶新的session請求。

當前 session 用戶數量

有時候我們系統需要統計當前在線用戶數量,這是我們就不能用cookie或者內存存儲session了,需要用數據庫存儲。

我們把session存儲到數據庫後,通過查詢當前沒過期session數量就可以確定在線用戶數

註銷session

我們在退出登錄時需要刪掉session,只需要調用 sesssion的 Delete()方法即可, 記得調用 Save方法保存操作

vue3 瀏覽器在關閉時發送退出登錄請求

在app.vue 文件中添加這樣的代碼, 其中:gapTime <= 12 中的 12 是經驗值。vue文件使用 setup語法糖模式

onMounted(() => {
    window.addEventListener('beforeunload', beforeunloadHandler)
    window.addEventListener('unload', unloadHandler)
})

function beforeunloadHandler (e) {
    beforeUnloadTime = new Date().getTime()
}

function unloadHandler (e) {
    gapTime = new Date().getTime() - beforeUnloadTime
    // 判斷是窗口關閉還是刷新
    console.log('====gapTime=======', gapTime)
    if (gapTime <= 12) {
        // 如果是登錄狀態,關閉窗口前,移除用戶
        navigator.sendBeacon(`/user/logout`, data) // 這裏data是請求的參數。一個字符串,具體查看navigator.sendBeacon 使用方法
    }
    debugger
}

session的Flashes講解

我們在看 session庫 源碼時會發現還有兩個函數:Flashes()AddFlash。簡單說一下這兩個函數作用

Flash既閃存,不是我們瀏覽器裏的那個flash插件,flash主要是存儲一下臨時數據。

  1. 我們調用 AddFlash時,會往flash裏存儲 一個鍵值對。
  2. 當我們調用 Flashs 讀取了 閃存內容後,這個閃存數據就被刪除了。(記得調用 Save()方法生效)

後記

本篇博文耗費了比較大的精力才研究透徹,如果對你有幫助,可以點個推薦,當然想請我喝杯可樂我也會很開心的哦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章