revel 的 session 是基于加密签名 cookie 的客户端存储机制,不支持服务端持久化;写入后必须显式调用 save() 才生效,删除需配合 save() 且不立即失效,过期与安全校验须由业务代码主动实现。

Revel 框架的 Session 不是服务端持久化会话,而是基于加密签名 Cookie 的客户端存储机制——这意味着你不能像在 Flask 或 Laravel 里那样依赖服务端 session store 做全局状态管理,所有“写入”只影响本次响应的 Cookie,下一次请求时会重新解密生成一个全新、独立的 c.Session 实例。
Session 写入后必须显式调用 Save()
Revel 的 c.Session 是一个 map-like 结构,但修改它不会自动同步到响应 Cookie 中。漏掉 Save() 是最常见导致登录失败的原因。
- 不生效的写法:
c.Session["user"] = "alice"(没调用Save(),下次请求读不到) - 正确写法:
c.Session["user"] = "alice"; c.Session.Save() -
Save()必须在响应写出前调用,放在return之后无效 - 如果使用
Redirect(),Save()必须在重定向前完成,否则跳转后 session 为空
Session 删除不是“立即失效”,而是“下次请求不可见”
Revel 的 session 删除(如 delete(c.Session, "user"))只作用于当前请求的响应 Cookie,不会清除客户端已有的旧 Cookie,也不会影响其他并发请求的 session 状态。
- 错误认知:调用
delete()就等于“登出成功”,后续请求立刻拿不到值 - 真实情况:只有配合
c.Session.Save()才会把删除操作写入新 Cookie;若用户仍持有旧 Cookie(比如多标签页未刷新),仍可能解析出旧值 - 安全登出建议:删除 key + 调用
Save()+ 设置Max-Age=0(需自定义 Cookie 写法,Revel 默认不支持,可改用c.Response.Out.Header().Set("Set-Cookie", ...)强制覆盖)
Session 过期逻辑必须由业务代码主动控制
Revel 默认不提供服务端 session 过期检查,也没有后台清理机制。所谓“60 秒验证码过期”,其实是靠每次请求时读取时间戳并比对实现的。
- 写入时存时间戳:
c.Session["captcha_ts"] = fmt.Sprintf("%d", time.Now().Unix()) - 读取前校验:
ts, _ := strconv.ParseInt(c.Session["captcha_ts"], 10, 64); if time.Since(time.Unix(ts, 0)) > 60*time.Second { /* 过期 */ } - 过期后应主动
delete(c.Session, "captcha_code"); delete(c.Session, "captcha_ts"); c.Session.Save() - 不要依赖
time.AfterFunc异步删 session —— 那个c.Session实例早已被回收,操作完全无效
权限判断别只看 Session 是否存在,要验证值的有效性
仅检查 c.Session["user"] != "" 是危险的。攻击者可以手动构造 Cookie,填入任意字符串绕过基础判断。
- 推荐做法:登录成功后,把用户 ID 和一个服务端生成的随机 token 组合签名(如
HMAC-SHA256(userID + salt + timestamp)),存入c.Session["auth_sig"] - 每次请求校验时,用相同逻辑重算签名,比对是否一致
- 或更简单:登录后查库确认用户状态(是否被禁用、是否需强制重绑),避免 session 成为唯一信任源
- 角色字段(如
c.Session["role"])必须和用户 ID 一起写入,并在每次鉴权时联合校验,防止篡改 role 单独生效
Revel 的 session 机制轻量高效,但它的“无状态”本质也意味着所有时效性、一致性、安全性逻辑都得由你亲手补全——没有魔法,只有明确的时间戳、显式的 Save()、以及每次请求都重走一遍的校验链。











