一.如何保持客户端和服务端的TCP连接
1.http协议是无状态的
HTTP 是一种不保存状态,即无状态(stateless)协议。HTTP 协议自身不对请求和响应之间的通信状态进行保存。也就是说在 HTTP 这个级别,协议对于发送过的请求或响应都不做持久化处理。

使用 HTTP 协议,每当有新的请求发送时,就会有对应的新响应产生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计成
如此简单的。
2.问题
当用户来到微博登陆页面,输入用户名和密码之后点击 "登录" 后浏览器将认证信息 POST 给远端的服务器,服务器执行验证逻辑,如果验证通过,则浏览器会跳转到登录用户的微博首页,在登录成功后,服务器如何验证我们对其他受限制页面的访问呢?
3.解决
因为 HTTP 协议是无状态的,所以很显然服务器不可能知道我们已经在上一次的 HTTP 请求中通过了验证。当然,最简单的解决方案就是所有的请求里面都带上用户名和密码,这样虽然可行,但大大加重了服务器的负担(对于每个 request 都需要到数据库验证),也大大降低了用户体验 (每个页面都需要重新输入用户名密码,每个页面都带有登录表单) 。既然直接在请求中带上用户名与密码不可行,那么就只有在服务器或客户端保存一些类似的可以代表身份的信息了,所以就有了 cookie 与 session。
二.Cookie
1.Cookie如何保持TCP连接
Cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie的首部字段信息,通知客户端保存 Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值后发送出去。
服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。

2. Cookie 的基本格式
一个 Cookie 是一行字符串,例如:
Set-Cookie: sessionid=abc123; Path=/; Domain=example.com; Max-Age=3600; HttpOnly; Secure; SameSite=Lax
Cookie = name=value + 属性(可选)
3. Cookie 各字段详细说明(最重要)
| 字段 | 作用 |
|---|---|
| name=value | Cookie 名和内容 |
| Domain | 允许发送 Cookie 的域名范围 |
| Path | 允许发送 Cookie 的 URL 路径范围 |
| Expires | 绝对过期时间 |
| Max-Age | 相对过期时间(优先于 Expires) |
| HttpOnly | JS 无法读取(防 XSS) |
| Secure | 仅 HTTPS 发送 |
| SameSite | 限制跨站请求(防 CSRF) |
4.cookie的生命周期
cookie 是有时间限制的,根据生命期不同分成两种:会话 cookie 和持久 cookie;
如果不设置过期时间,则表示这个 cookie 的生命周期为从创建到浏览器关闭为止,只要关闭浏览器窗口,cookie 就消失了。这种生命期为浏览会话期的 cookie 被称为会话 cookie。会话 cookie 一般不保存在硬盘上而是保存在内存里。
如果设置了过期时间 (setMaxAge (606024)),浏览器就会把 cookie 保存到硬盘上,关闭后再次打开浏览器,这些 cookie 依然有效直到超过设定的过期时间。存储在硬盘上的 cookie 可以在不同的浏览器进程间共享,比如两个 IE 窗口。而对于保存在内存的 cookie,不同的浏览器有不同的处理方式。
Set-Cookie: token=abc; Max-Age=3600
5. Domain(域名作用范围)
表示哪些域名会携带 Cookie。
① 不写 Domain(默认)
Cookie 只对当前域名有效。
例:访问 login.example.com
Cookie 默认只在:login.example.com 有效。
② 写 Domain(含子域名)
Set-Cookie: user=abc; Domain=example.com
Cookie 适用于:
-
example.com -
www.example.com -
api.example.com -
其所有子域
6. Path(路径作用范围)
指定在哪些路径下发送 Cookie。
Set-Cookie: id=123; Path=/admin
则访问:
-
/admin -
/admin/user -
/admin/settings
才会带 Cookie。
7. HttpOnly(防止 JS 读取)
Set-Cookie: token=abc; HttpOnly
特点:
-
前端 JS 无法通过
document.cookie获取 -
只能浏览器自动发送到后端
-
防止 XSS 盗取 Session
这是存放 Session ID 时必开!
8. Secure(仅 HTTPS 模式发送)
Set-Cookie: session=xxx; Secure
HTTP 不发送此 Cookie,HTTPS 才发送确保不会被窃听。
9. SameSite(防止 CSRF)
Cookie 最重要的安全特性之一。
| SameSite 值 | 特性 |
|---|---|
| Strict | 完全禁止第三方请求携带 Cookie |
| Lax | 部分允许(GET 允许)推荐 |
| None | 跨站请求完全允许(必须配合 Secure) |
示例:
Set-Cookie: id=123; SameSite=Lax
建议后端 Session Cookie 使用:
SameSite=Lax; HttpOnly; Secure
三.Session
1. Session 是什么?
Session 是服务端记录用户状态的数据结构(会话)。
核心目的:
在 HTTP 无状态协议上维持用户登录状态、购物车等用户上下文信息。
例如:
-
用户登录后服务器记录:
userID=123 -
下次请求携带 Session ID → 服务器就能识别用户
Session 是:
-
存放在服务器
-
属于每个用户的独立数据空间
2. Session 与 Cookie 的关系
浏览器不会自动知道 Session,需要一个识别标识:Session ID。
流程:
-
用户第一次访问
-
服务器生成 Session + 唯一 SessionID
-
服务器通过 Set-Cookie 发给浏览器:
Set-Cookie: sessionid=xyz123; HttpOnly; Path=/
4.浏览器之后的每次请求都会带上:Cookie: sessionid=xyz123
5.服务器根据 SessionID 找到对应的 Session 数据
➡ Cookie 存 SessionID
➡ Session 存用户信息
两者紧密合作。
3. Session 内部结构(常见实现)
Session 就是一份键值数据:
session["user_id"] = 1001 session["role"] = "admin" session["cart"] = [...]
数据类型通常是 map 或对象。
Go 中 session 示例:
session.Values["userID"] = 1001
4. Session 的生命周期
Session 过期有两个主要维度:
① Session 数据多久过期
例如 30 分钟不活跃 → 销毁 Session
② SessionID 的 Cookie 过期时间
例如 Cookie Max-Age=3600 → 1 小时后浏览器不再发送 SessionID
Session 过期一般由服务端维护:
-
Redis 过期
-
内存 Map 定时清理
-
文件 session 删除
5. Session ID 的生成方式
Session ID 是 随机的高强度 Token,通常:
-
16--32 字节随机数
-
Base64 或 hex 编码
-
不可预测(防止会话劫持)
Go 示例:
id := make([]byte, 32) rand.Read(id) sessionID := hex.EncodeToString(id)
6. Session 存储方式(重要)
常见存储方式:
| 存储方式 | 优点 | 缺点 |
|---|---|---|
| 内存(map) | 快 | 重启丢失,不支持多机 |
| 文件系统 | 简单 | 慢,不适合集群 |
| Redis(推荐) | 快、可靠、支持集群 | 需要维护 Redis |
| 数据库(MySQL) | 稳定、持久化 | 读写速度比 Redis 慢 |
大型系统几乎都用 Redis。
7. Session 工作流程
- 用户第一次访问:
Browser ----> Server - 服务器创建 Session:
sessionID="abc123" session["user_id"]=1001 store.save(session) - 服务器发回 Set-Cookie:
Set-Cookie: sessionid=abc123; HttpOnly - 第二次访问:
Browser(cookie: sessionid=abc123) ---> Server - 服务器查询 Session:
store.get("abc123") - 找到了 user_id → 用户已登录。
四.session+cookie
1.Session 是如何通过 Cookie 保存 Session ID 的?
(1)用户第一次访问服务器
用户请求:
GET /login
服务器发现用户 没有 SessionID(因为没有 Cookie),于是:
(2)服务器生成一个唯一的 SessionID
通常是随机的 16--32 字节 token,例如:
sessionID = "abc123xyz890"
(3)服务器创建 Session 对象
session 存在服务端:
Session["abc123xyz890"] = { "userID": 1001, "role": "admin", "cart": [...] }
(存储位置可能是内存、Redis、数据库等)
(4)服务器将 SessionID 写入 Cookie 返回给浏览器
HTTP 响应头:
Set-Cookie: sessionid=abc123xyz890; Path=/; HttpOnly
浏览器看到 Set-Cookie 后,会将此键值对记录到本地 Cookie 存储中。
(5)浏览器本地 Cookie 的存放位置
不同浏览器存放位置不同,但都是:
-
持久存储在用户电脑硬盘(如果有 Expires/Max-Age)
-
或存放在内存(Session Cookie)
实际存储路径由浏览器决定,例如:
-
Chrome:SQLite 数据库文件
-
Firefox:cookies.sqlite
-
Safari:内部 key-value 存储
开发者不可直接操作该文件,只能通过 HTTP 交互或 JavaScript API。
(6)浏览器之后访问时会自动携带 Cookie
访问 server:
GET /profile Cookie: sessionid=abc123xyz890
浏览器自动把以前保存的 sessionid 附带在请求头里。
(7)服务器收到 sessionid 后,从服务端找到 session 数据
服务器做:
session = SessionStore["abc123xyz890"]
找到了用户会话数据:
{ "userID": 1001, "role": "admin" }→ 用户身份识别成功。
2.服务端如何发送这个 session 的唯一标识?
Cookie
服务端通过设置 Set-cookie 头就可以将 session 的标识符传送到客户端,而客户端此后的每一次请求都会带上这个标识符,另外一般包含 session 信息的 cookie 会将失效时间设置为 0 (会话 cookie),即浏览器进程有效时间。至于浏览器怎么处理这个 0,每个浏览器都有自己的方案,但差别都不会太大 (一般体现在新建浏览器窗口的时候);
URL 重写
所谓 URL 重写,就是在返回给用户的页面里的所有的 URL 后面追加 session 标识符,这样用户在收到响应之后,无论点击响应页面里的哪个链接或提交表单,都会自动带上 session 标识符,从而就实现了会话的保持。虽然这种做法比较麻烦,但是,如果客户端禁用了 cookie 的话,此种方案将会是首选。
五.在go中使用Cookie
Go 设置 cookie
Go 语言中通过 net/http 包中的 SetCookie 来设置:
http.SetCookie(w ResponseWriter, cookie *Cookie)
w 表示需要写入的 response,cookie 是一个 struct,让我们来看一下 cookie 对象是怎么样的
我们来看一个例子,如何设置 cookie
Go
type Cookie struct {
Name string
Value string
Path string
Domain string
Expires time.Time
RawExpires string
// MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
// MaxAge>0 means Max-Age attribute present and given in seconds
MaxAge int
Secure bool
HttpOnly bool
Raw string
Unparsed []string // Raw text of unparsed attribute-value pairs
}
Go
expiration := time.Now()
expiration = expiration.AddDate(1, 0, 0)
cookie := http.Cookie{Name: "username", Value: "astaxie", Expires: expiration}
http.SetCookie(w, &cookie)
Go 读取 cookie
上面的例子演示了如何设置 cookie 数据,我们这里来演示一下如何读取 cookie
cookie, _ := r.Cookie("username")
fmt.Fprint(w, cookie)
还有另外一种读取方式
for _, cookie := range r.Cookies() {
fmt.Fprint(w, cookie.Name)
}
可以看到通过 request 获取 cookie 非常方便。
六.go中如何使用Session
1.Go 中使用 Session 的方式有哪些
Go 标准库 没有内置 Session 机制,但常用:
-
gorilla/sessions(最流行,稳定)
-
gin-contrib/sessions(Gin 框架专用)
-
自定义 Session(自己实现)
2.session 管理设计
我们知道 session 管理涉及到如下几个因素
- 全局 session 管理器
- 保证 sessionid 的全局唯一性
- 为每个客户关联一个 session
- session 的存储 (可以存储到内存、文件、数据库等)
- session 过期处理
3.用 gorilla/sessions 在 Go 中使用 Session(推荐)
首先安装:
go get github.com/gorilla/sessions
示例一:基于 Cookie 的 Session(Session 数据存在客户端)
不安全数据不能放在 Session 中,适合小项目或简单场景。
- 初始化 Session Store
var store = sessions.NewCookieStore([]byte("very-secret-key"))
- 设置 Session(用户登录时)
Go
func Login(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-id")
// 设置会话数据
session.Values["user"] = "Alice"
session.Values["role"] = "admin"
// 保存到客户端 Cookie(带签名,防篡改)
session.Save(r, w)
fmt.Fprintln(w, "login success")
}
- 读取 Session(用户访问受保护页面)
Go
func Profile(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-id")
user, ok := session.Values["user"].(string)
if !ok {
fmt.Fprintln(w, "Not logged in")
return
}
fmt.Fprintf(w, "Hello %s", user)
}
- 删除 Session(用户退出登录)
Go
func Logout(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-id")
session.Options.MaxAge = -1 // 删除 Cookie
session.Save(r, w)
fmt.Fprintln(w, "logout success")
}
CookieStore 的 Session 数据特点
-
数据 不在服务器存
-
Session 值会被 加密 / 签名 放在 Cookie
-
大数据不适合
-
不适用于安全性要求高的场景(存敏感数据不安全)
4.使用 Redis 作为 Session 存储(大型系统推荐)
这种方式安全、支持分布式,是生产最常用方式。
安装:
go get github.com/gorilla/sessions
go get github.com/boj/redistore/v2
初始化 Redis SessionStore
Go
import (
"github.com/gorilla/sessions"
redistore "github.com/boj/redistore/v2"
)
var store *redistore.RediStore
func init() {
var err error
store, err = redistore.NewRediStore(
10,
"tcp",
"127.0.0.1:6379",
"",
[]byte("secret-key"),
)
if err != nil {
panic(err)
}
}
设置 Session(登录)
Go
func Login(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-id")
session.Values["userID"] = 1001
session.Values["role"] = "admin"
session.Save(r, w)
fmt.Fprintln(w, "login success")
}
读取 Session
Go
func Home(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-id")
userID := session.Values["userID"]
if userID == nil {
fmt.Fprintln(w, "not logged in")
return
}
fmt.Fprintf(w, "Hello user: %v", userID)
}
删除 Session(退出登录)
Go
func Logout(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-id")
session.Options.MaxAge = -1
session.Save(r, w)
fmt.Fprintln(w, "logout success")
}
5.Session 工作原理(帮助理解)
gorilla/sessions 的原理:
-
登录时
-
服务端创建 Session 对象
-
生成随机 SessionID
-
存到 Redis / 内存 / CookieStore
-
把 SessionID 写入 Cookie
Set-Cookie返回客户端
-
-
客户端之后访问时
浏览器发送:
Cookie: session-id=abc123 -
服务器根据 session-id 查询真实 Session 内容
6.手写一个最简单的 Session 系统(理解原理)
仅演示核心逻辑:
Go
var sessionStore = map[string]map[string]any{}
func newSessionID() string {
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)
}
设置 Session
Go
func SetSession(w http.ResponseWriter, username string) {
sid := newSessionID()
sessionStore[sid] = map[string]any{
"user": username,
}
// 写入 Cookie
http.SetCookie(w, &http.Cookie{
Name: "sid",
Value: sid,
Path: "/",
})
}
获取 Session
Go
func GetSession(r *http.Request) map[string]any {
cookie, err := r.Cookie("sid")
if err != nil {
return nil
}
return sessionStore[cookie.Value]
}
7.总结(Go 中使用 Session)
| 方式 | 特点 | 场景 |
|---|---|---|
| CookieStore | 数据存在 Cookie(签名加密) | 小项目 |
| Redis Store(推荐) | 数据存在 Redis,支持分布式 | 大型网站、后台 |
| map 手写 Session | 理解机制 | 学习用 |
Session 的核心流程永远是:
服务端生成 SessionID → 写入 Cookie → 客户端每次访问带上 → 服务端查 Session 数据