尧图精选

beego Session 模块(server/web/session)

🕒 发布时间:2026/9/15 7:30:09 📁 来源:尧图网络
1. 三级架构Manager(每应用一个,GlobalSessions) │ 持有 provider(按配置选择的引擎) ▼ Provider(每引擎一个,会话仓库) │ SessionRead(sid) 创建/取出 ▼ Store(每请求一个) │ Get/Set/Delete/Flush… ▼ SessionRelease(请求结束,写回仓库)接口定义(session.go)type Store interface { // :46-54 Set(ctx, key, val) error Get(ctx, key) interface{} Delete(ctx, key) error SessionID(ctx) string SessionReleaseIfPresent(ctx, w) # 2.x 新增:防御性释放 SessionRelease(ctx, w) # 写回持久层 Flush(ctx) error # 清空 } type Provider interface { // :58-66 SessionInit(ctx, gcLifetime, config) error # 引擎初始化(连接串) SessionRead(sid) (Store, error) SessionExist(sid) (bool, error) SessionRegenerate(oldSID, newSID) (Store, error) SessionDestroy(sid) error SessionAll() int SessionGC(ctx) # 引擎各自的清理 }注册表(session.go:75-89)provides map[string]ProviderRegister(name, provide);12 个引擎各自 init 注册:cookie(file: sess_cookie.go)、file、mem、redis、redis_cluster、redis_sentinel、memcache、mysql、postgres、couchbase、ledis、ssdb。2. Manager(session.go:92-144)type Manager struct { provider Provider config *ManagerConfig // SessionName/CookieLifeTime/Gclifetime/Domain/SameSite... }NewManager(provideName, cf)(109-144):查 provides、SessionInit(provider)、校验 header 模式 MIME。web 侧 hooks.go registerSession(47-77):按 BConfig 构建 ManagerConfig → 创建GlobalSessions→go GlobalSessions.GC()。2.1 sid 从哪来:getSid(158-184)三级回退:Cookie → URL query → HTTP header(后两者需 SessionEnableSidInURLQuery/HeaderIP 开启)。这就是App 客户端无 cookie 也能带 session的实现。2.2 SessionStart(188-238)——每请求入口getSid 取 sid ├─ 有效:provider.SessionRead(sid) └─ 无效:crypto/rand 生成 16 字节 hex(SessionIDPrefix 前缀,345-352) provider.SessionRead(new sid) 写 cookie:HttpOnly(!SessionDisableHTTPOnly)/Secure/Domain/SameSite/CookieLifeTime (SessionEnableSidInHTTPHeader 时也写响应头) 返回 Store → serveHttp 放入 ctx.Input.CruSession2.3 SessionRegenerateID(284-333)换 sid 保数据:provider.SessionRegenerate(oldsid, newsid) 重写 cookie——防会话固定攻击的服务端支持。2.4 GC 自调度(278-281)——优雅的技巧func (manager *Manager) GC() { manager.provider.SessionGC(context.Background()) time.AfterFunc(manager.config.Gclifetime, func() { manager.GC() }) }不用常驻 ticker,每次执行完后安排下一次:改 Gclifetime 配置能在下轮生效,且无泄漏。值得抄的模式。2.5 会话释放的演进v2.0:defer SessionRelease(ctx, w)近期修复(git log: use request context for session release)新增SessionReleaseIfPresent:以请求 context 为准,请求 ctx 已取消时也能安全释放,避免客户端断连导致会话丢写。各引擎同步实现。3. cookie 引擎:客户端会话的安全样本(sess_cookie.go sess_utils.go)存储介质是 cookie 本身(4KB 限制),数据链:写入:map[string]interface{} → gob 编码(EncodeGob,47-70) → AES-CTR 加密(encrypt,87-116;密钥hash(session 安全串)) → base64 → 编码 cookie: name|date|value| 三段 base64 HMAC-SHA256 签名(encodeCookie,118-152) 读取:decodeCookie(154-188) → 校验 MAC(防篡改) → 校验 date 在 [now-gcmaxlifetime, now](防重放旧值) → 解 base64 → AES 解密 → gob 解码为什么三层:签名防篡改、加密防偷看(明文 key)、时间戳防重放。虽然单值 cookie 一般不建议存敏感数据,但这套编码是 beego 自身 SecureCookie(context 包)的同源实现。4. file 引擎走读(sess_file.go)——引擎范本type FileSessionStore struct { sid; lock sync.RWMutex; values map[...]; ... } func (fs *FileSessionStore) SessionRelease(ctx, w) { // gob 编码 values → 写回 sid 文件(带文件锁) } type FileProvider struct { maxlifetime; savePath } func (fp *FileProvider) SessionRead(sid) { // 打开/创建 savePath/sid,独占锁;空文件初始化 } func (fp *FileProvider) SessionGC(ctx) { // filepath.Walk:按 ModTime maxlifetime 删除过期文件(gcpath,302) }redis/mysql 引擎同构:ProviderConfig 连接串,SessionRead 建连接取 hash/行,SessionRelease 写回,GC 用 EXPIRE/DELETE。读引擎源码的套路:只看 SessionInit/Read/Release/GC 四个方法。5. 与 serveHttp 的协作(S05 回顾)步骤 7:FindRouter 预判 sessionOn(路由级开关) GlobalSessions.SessionStart → ctx.Input.CruSession defer SessionReleaseIfPresent 控制器/过滤器:Get/Set(内存 values) 步骤 17 后:defer 触发 SessionRelease → provider 写回写延迟语义:会话修改在请求结束才持久化——意味着同一用户并发两请求存在丢失更新(file 引擎整文件覆盖)。需要强一致计数请用 redis 引擎的原子命令或 DB 行锁。6. 内存引擎的 map 锁(sess_mem.go)CookieSessionStore/mem 引擎:RWMutex 保护 values;SessionAll 遍历计数。单机开发够用,多副本部署必换外部引擎(使用篇 Day08 坑 2 的根源)。7. 设计赏析三级接口职责清晰:Manager(策略/sid/cookie)、Provider(仓储)、Store(读写);新引擎 4-7 个方法搞定。GC 的 AfterFunc 自调度:见 2.4。sid 多通道(cookie/query/header):移动端友好,代价是 XSS/URL 泄漏面变大——默认只开 cookie。防御性释放(SessionReleaseIfPresent):一个真实生产 bug 催化的 API 演进,读 git 历史比读代码更能理解它为什么存在。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →