Cookie,Session的介绍和如何保持TCP连接

一.如何保持客户端和服务端的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 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。

一个 Cookie 是一行字符串,例如:

Set-Cookie: sessionid=abc123; Path=/; Domain=example.com; Max-Age=3600; HttpOnly; Secure; SameSite=Lax

Cookie = name=value + 属性(可选)


字段 作用
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 是:

  • 存放在服务器

  • 属于每个用户的独立数据空间


浏览器不会自动知道 Session,需要一个识别标识:Session ID

流程:

  1. 用户第一次访问

  2. 服务器生成 Session + 唯一 SessionID

  3. 服务器通过 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

例如 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 工作流程

  1. 用户第一次访问:Browser ----> Server
  2. 服务器创建 Session:sessionID="abc123" session["user_id"]=1001 store.save(session)
  3. 服务器发回 Set-Cookie:Set-Cookie: sessionid=abc123; HttpOnly
  4. 第二次访问:Browser(cookie: sessionid=abc123) ---> Server
  5. 服务器查询 Session:store.get("abc123")
  6. 找到了 user_id → 用户已登录。

四.session+cookie

(1)用户第一次访问服务器

用户请求:

GET /login

服务器发现用户 没有 SessionID(因为没有 Cookie),于是:

(2)服务器生成一个唯一的 SessionID

通常是随机的 16--32 字节 token,例如:

sessionID = "abc123xyz890"

(3)服务器创建 Session 对象

session 存在服务端:

Session["abc123xyz890"] = { "userID": 1001, "role": "admin", "cart": [...] }

(存储位置可能是内存、Redis、数据库等)


HTTP 响应头:

Set-Cookie: sessionid=abc123xyz890; Path=/; HttpOnly

浏览器看到 Set-Cookie 后,会将此键值对记录到本地 Cookie 存储中。


不同浏览器存放位置不同,但都是:

  • 持久存储在用户电脑硬盘(如果有 Expires/Max-Age)

  • 或存放在内存(Session Cookie)

实际存储路径由浏览器决定,例如:

  • Chrome:SQLite 数据库文件

  • Firefox:cookies.sqlite

  • Safari:内部 key-value 存储

开发者不可直接操作该文件,只能通过 HTTP 交互或 JavaScript API。


访问 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 语言中通过 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)

上面的例子演示了如何设置 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 机制,但常用:

  1. gorilla/sessions(最流行,稳定)

  2. gin-contrib/sessions(Gin 框架专用)

  3. 自定义 Session(自己实现)

2.session 管理设计

我们知道 session 管理涉及到如下几个因素

  • 全局 session 管理器
  • 保证 sessionid 的全局唯一性
  • 为每个客户关联一个 session
  • session 的存储 (可以存储到内存、文件、数据库等)
  • session 过期处理

3.用 gorilla/sessions 在 Go 中使用 Session(推荐)

首先安装:

go get github.com/gorilla/sessions


不安全数据不能放在 Session 中,适合小项目或简单场景。

  1. 初始化 Session Store

var store = sessions.NewCookieStore([]byte("very-secret-key"))


  1. 设置 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")
}

  1. 读取 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)
}

  1. 删除 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 的原理:

  1. 登录时

    • 服务端创建 Session 对象

    • 生成随机 SessionID

    • 存到 Redis / 内存 / CookieStore

    • 把 SessionID 写入 Cookie Set-Cookie 返回客户端

  2. 客户端之后访问时

    浏览器发送:

    Cookie: session-id=abc123

  3. 服务器根据 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 数据

相关推荐
专业开发者2 小时前
Wi-Fi®:可持续的优选连接方案
网络·物联网
GIS数据转换器3 小时前
综合安防数智管理平台
大数据·网络·人工智能·安全·无人机
chem41114 小时前
魔百盒 私有网盘seafile搭建
linux·运维·网络
lang201509284 小时前
Sentinel核心:ClusterNode全局资源统计解析
网络·python·sentinel
Wang's Blog4 小时前
Elastic Stack梳理:深入解析Packetbeat网络抓包与Heartbeat服务监控
网络·elasticsearch·搜索引擎
LYFlied5 小时前
前端性能优化:成本收益权衡下的实践路径
前端·网络·面试·性能优化·打包构建·页面加载链路
wusam5 小时前
计算机网络传输层应用层综合实验3:telnet远程访问服务部署
服务器·网络·计算机网络·应用层服务部署
北京盟通科技官方账号5 小时前
Ixxat Mobilizer系列:助力汽车组件的高效下线测试
网络协议·机器人·自动化·汽车·制造
此生只爱蛋5 小时前
【Linux】TCP机制
网络·网络协议·tcp/ip
Tim风声(网络工程师)5 小时前
一台电脑给另一台电脑提供网络
运维·服务器·网络