Cookie&Session(Go实现)
Go视角下的Cookie和Session
会话管理一直是做web开发中绕不开的问题,对于某个会话,建立,维护,关闭,一直是会话管理的核心问题,今天我们聊cookie和session,正是为解决此类问题而生。
cookie
什么是cookie
Cookie 是 Web 服务器生成并发送至 Web 浏览器的小型信息文件。Web 浏览器在预定义的时间段内或在用户在网站上的会话期间存储收到的 Cookie。它们将相关的 Cookie 附加到用户今后向 Web 服务器提出的任何请求中。
Cookie 帮助网站了解用户的情况,使网站能够提供个性化的用户体验。例如,电子商务网站使用 Cookie 来了解用户在其购物车中放置了哪些商品。此外,一些 Cookie 对于维护安全必不可少,如身份验证 Cookie(见下文)。
在互联网上使用的 Cookie 也称为“HTTP Cookie”。与大部分网络一样,Cookie 是使用 HTTP 协议发送的。
简单概括一下吧,当客户端发送http请求给服务器的时候,它会将所有相关的cookie信息包含在请求头中的cookie字段中随数据一并发送,这样,服务器接收到数据的时候,就可以通过解析cookie的内容进一步提供个性化内容和服务。
session
什么是session(会话)
会话是一种持久的网络协议,用于完成服务器与客户端之间的一些交互动作,会话是一个比连接颗粒度更大的概念,一次会话可能包含多次连接,每次连接被认为是会话的一次操作
在Web中,Session是指一个用户与网站服务器进行一系列交互的持续时间,通常指从注册进入系统到注销退出系统之间所经过的时间,以及在这段时间内进行的操作,还有,服务器端为保存用户状态开辟的存储空间。
http的无状态性
每条http请求/相应是互相独立的,服务器并不知道两条http请求是同一用户发送的还是不同用户发送的,就好比生活中常见的饮料自动贩售机,投入硬币,选择饮料,然后饮料出来,买饮料的人拿到饮料,整个过程中贩售机只负责识别要买的是哪种饮料并且给出饮料,并没有记录是哪个人买的,以及某个人买了哪几种,每种买了几瓶。如果考虑现在的购物网站的购物车功能,记录用户状态就很有必要了,这就需要用到会话,而http协议由于本身的无状态性就需要cookie来实现会话。
既然如此,对于某个请求,服务器如何确定用户状态呢(如用户是否已经登录)?
其实这个过程非常好理解
当用户第一次访问网站时,服务器对用户没有任何了解,于是通过http相应给用户分发了一个专属于这个用户的cookie,当用户收到这个应答时,浏览器会存下这个cookie,接下来当用户每次向这个服务器发送请求的时候,都携带上这个cookie,服务器收到这个cookie的时候就可以唯一标识用户。
服务器如何唯一标识用户呢?这里就又回到了我们的session,对于session,当我们的用户发送一个http请求开启一段对话的时候,客户端第一次收到这个http请求会在内存中开辟一块空间给这个会话,存储了这个会话的一些细节,其中最重要的就是标识用户的sessionID,这个ID唯一标识了这个session的所有者的ID,同时在转发http的时候cookie“唯一标识用户”的特征也体现于此(cookie携带session ID),需要注意的是,我们的客户端其实不会解析cookie,也没有必要解析cookie,收到来自服务器的http回复的时候cookie会自动保存至浏览器,待下次发出请求时会自动携带或选择手动控制,举个简单的例子
// 使用 XMLHttpRequest 发起 GET 请求
var xhr = new XMLHttpRequest();
xhr.open('GET', '/profile', true);
xhr.withCredentials = true; // 允许发送包含凭据的请求(即带有 Cookie)
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
xhr.send();
而作为客户端,也无权解析cookie,在cookie发送过来时,即使数据在不使用https的情况下是明文传输,但cookie与之不同则在服务端已经进行过加密处理,客户端也无法对其解析。这一步的主要目的就是保证传输过程中会话的安全性,减小因session ID泄漏导致伪造,篡改请求的可能。
那么到这我们也许大概理解了服务器端是如何维护会话的,几个关键词一定要记住,面试高频
Cookie
-
存储位置:客户端(浏览器)
-
生命周期:Cookie 可以有不同的有效期,可以是会话级(浏览器关闭后失效)或持久化(通过设置过期时间)。
-
用途 :如上文
-
数量级:每个Cookie的数量有限,通常不超过4KB,每个域名下最多存储20个Cookie
Session
-
存储位置:session数据存储在服务器端。
-
生命周期:Session 的生命周期通常由服务器控制,常见的情况是用户关闭浏览器或在一定时间不活动后会话过期。
-
用途:如上文
-
数据量限制:取决于服务器端存储能力
eg:场景举例:登录系统:
-
用户登录时,服务器验证用户身份并创建一个 Session,生成一个唯一的 Session ID。
-
服务器将 Session ID 通过 Set-Cookie 响应头发送给客户端,存储在浏览器的 Cookie 中。
-
客户端每次请求时都会携带这个 Session ID,服务器通过 Session ID 查找和维护用户会话状态。
既然我们今天的标题是Go视角下的,那么我们肯定不能只讲理论,看一个例子吧!
场景就拿eg来说,先来看代码
package main
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"net/http"
)
type LoginRequest struct {
UID string `json:"userid"`
UserName string `json:"username"`
Password string `json:"password"`
}
func main() {
r := gin.Default()
// 创建 session 存储
store := cookie.NewStore([]byte("moyn8y9abnd7q4zkq2m73yw8tu9j5ixm"))
r.Use(sessions.Sessions("mysession", store))
// 设置 Session
r.POST("/set", func(c *gin.Context) {
var userJSON LoginRequest
if err := c.ShouldBindJSON(&userJSON); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"resgiste erro": err.Error()})
return
}
sessionStore := sessions.Default(c)
sessionStore.Set("user_id", userJSON.UID)
sessionStore.Set("token", uuid.NewString())
sessionStore.Save()
c.JSON(http.StatusOK, gin.H{"message": "Session set"})
})
// 获取 Session
r.POST("/get", func(c *gin.Context) {
session := sessions.Default(c)
userID := session.Get("user_id")
token := session.Get("token")
if userID == nil || token == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"user_id": userID, "token": token})
})
// 清除 Session
r.POST("/clear", func(c *gin.Context) {
session := sessions.Default(c)
session.Clear()
session.Save()
c.JSON(http.StatusOK, gin.H{"message": "Session cleared"})
})
// 启动服务
r.Run(":8082")
}
对于这段代码,我们可以看到有三个路由分别是建立用户session,查询用户是否登录,以及删除用户。
在收到用户post请求登录的请求的时候,我们通常在此建立session,其中的session ID由服务端制定,通常为了保证session ID唯一,我们采用自增的办法来设置,其次,这里一定要注意在定义session前分配给cookie,(如果我们不使用cookie可以使用memstore,这里不过多介绍区别),并声明secret(密匙)。后面就很简单了,一些简单的声明操作。值的注意的是session.Save(),这里将session保存到了我们的物理内存中,优点就是非常快,如果客户端数量庞大,我们就要考虑数据库什么的了,其实道理是一样的。
测试一下:
setp1:设置session
step2:获取用户状态
step3:清空session
step4:清空后查看用户状态
至此,我们可以初步了解服务端通过session和cookie协作维护用户状态了。
下一章我们讲一下token,JWT相关(push一下自己)