在 Web 开发中,HTTP 协议是无状态的,每一次请求都是独立的。为了让服务器记住客户端的状态,我们需要使用 Cookie 和 Session。
- • Cookie:存储在浏览器端的小数据,随请求一起发送给服务器。
- • Session:存储在服务器端的用户会话数据,通常通过 Cookie 中的 Session ID 来关联。
一、案例目标
我们要实现一个简单的会话系统:
- 1. 用户第一次访问
/set
接口时,服务器设置一个 Cookie 记录用户名。 - 2. 用户访问
/get
接口时,服务器读取 Cookie 并返回用户信息。 - 3. 使用内存保存 Session 数据,让服务器记住用户的登录状态。
二、核心知识点
- • 设置 Cookie:
http.SetCookie(w, cookie)
- • 读取 Cookie:
r.Cookie("name")
- • Session 存储(简单版):用
map[string]string
在内存中存储用户会话数据 - • Session ID 生成:用
crypto/rand
生成随机字符串
三、完整代码示例
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"sync"
)
// 内存版 Session 存储
var (
sessionStore = make(map[string]string)
mu sync.Mutex
)
// 生成随机 Session ID
func generateSessionID() string {
b := make([]byte, 16)
_, _ = rand.Read(b)
return hex.EncodeToString(b)
}
// 设置 Session 和 Cookie
func setHandler(w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username")
if username == "" {
username = "游客"
}
// 创建 Session ID
sessionID := generateSessionID()
// 保存到服务器端 Session
mu.Lock()
sessionStore[sessionID] = username
mu.Unlock()
// 设置 Cookie
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: sessionID,
Path: "/",
})
fmt.Fprintf(w, "Session 已创建,欢迎你:%s\n", username)
}
// 获取 Session
func getHandler(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "未找到会话,请先访问 /set", http.StatusUnauthorized)
return
}
mu.Lock()
username, exists := sessionStore[cookie.Value]
mu.Unlock()
if !exists {
http.Error(w, "会话已失效", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "欢迎回来,%s!\n", username)
}
func main() {
http.HandleFunc("/set", setHandler)
http.HandleFunc("/get", getHandler)
fmt.Println("服务器启动:http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
四、运行与测试
- 1. 启动服务器:
go run main.go
- 2. 设置 Session(访问
/set
):
curl "http://localhost:8080/set?username=Tom"
返回:
Session 已创建,欢迎你:Tom
同时浏览器或客户端会收到一个 session_id
Cookie。
- 3. 获取 Session(访问
/get
):
curl "http://localhost:8080/get" --cookie "session_id=xxxx"
返回:
欢迎回来,Tom!
五、运行原理
- 1. 第一次访问
/set
- • 服务器生成
session_id
- • 在内存中保存
session_id -> username
映射 - • 设置 Cookie 让浏览器保存
session_id
- • 服务器生成
- 2. 访问
/get
- • 从 Cookie 读取
session_id
- • 查询服务器端 Session 存储
- • 找到对应用户名并返回
- • 从 Cookie 读取
六、注意事项
- 1. 内存版 Session 仅适合学习和测试
- • 生产环境要使用 Redis、数据库等持久化存储
- 2. Session 过期处理
- • 生产环境要设置超时机制(可用
time.AfterFunc
定时清理)
- • 生产环境要设置超时机制(可用
- 3. 安全性
- • 使用 HTTPS 传输 Cookie(
Secure: true
) - • 防止 XSS、CSRF 攻击
- • 使用 HTTPS 传输 Cookie(
七、进阶扩展
- • 使用 Go 第三方库(如
gorilla/sessions
)管理 Session - • 将 Session 存储到 Redis,支持分布式部署
- • 在 Cookie 中设置 HttpOnly、Secure 属性提升安全性
- • 实现 Session 超时自动清理机制