refactor: JWT 无状态认证替换为服务端 Session,修复记住我掉线问题
- 新增 internal/session 包:Session 结构体、Store 接口、MemoryStore(内存+后台清理)、RedisStore 预留 - Cookie 从 3 个精简为 1 个 mlb_sid(HttpOnly),移除 JWT access/refresh cookie - 会话过期采用滑动窗口:每次请求自动续期,记住我 30 天无操作过期 - AuthMiddleware/AuthAdmin/Maintenance 中间件改用 SessionManager.Validate() - AuthService Login/Register/ConfirmRestore 去除 token 生成,返回 *model.User - Controller 层在登录/注册后调用 SessionManager.Create 创建会话 - AdminService UpdateUserStatus/ResetUserToken 同步销毁 session 实现即时退登 - 改密/注销时通过 DestroyByUID 删除所有 session(替换 TokenVersion 检查) - config.yaml 新增 session 配置段(idle_timeout/remember_timeout/cleanup_interval) - TokenService/auth_parser/auth_token 标记废弃,移除 JWT 到期字段依赖
This commit is contained in:
@ -11,52 +11,30 @@ import (
|
||||
|
||||
// Cookie 名称常量
|
||||
const (
|
||||
CookieName = "mlb_token"
|
||||
RefreshCookieName = "mlb_refresh"
|
||||
RMCookieName = "mlb_rm" // 记住我标记,JS 可读
|
||||
SessionCookieName = "mlb_sid" // 会话 ID Cookie(HttpOnly)
|
||||
)
|
||||
|
||||
// SetAuthCookies 设置 access + refresh Cookie
|
||||
// rememberMe=true → Cookie 持久化(30天),关浏览器后仍保持登录
|
||||
// rememberMe=false → Cookie 用 session 模式(maxAge=0),关浏览器即清除,但页面开启期间自动刷新
|
||||
func SetAuthCookies(c *gin.Context, accessToken, refreshToken string, rememberMe bool, cfg *config.Config) {
|
||||
// SetSessionCookie 写入会话 ID Cookie
|
||||
// rememberMe=true → Cookie 持久化(30天),关浏览器后仍保持登录
|
||||
// rememberMe=false → Cookie session 模式(maxAge=0),关浏览器即清除
|
||||
func SetSessionCookie(c *gin.Context, sid string, rememberMe bool, cfg *config.Config) {
|
||||
secure := cfg.Server.Mode != "debug"
|
||||
c.SetSameSite(http.SameSiteLaxMode)
|
||||
|
||||
setCookie(c, CookieName, accessToken, cfg.JWT.AccessExpire*60, secure)
|
||||
|
||||
var maxAge int
|
||||
if rememberMe {
|
||||
setCookie(c, RefreshCookieName, refreshToken, int(cfg.JWT.RememberExpire*3600), secure)
|
||||
setPlainCookie(c, RMCookieName, "1", int(cfg.JWT.RememberExpire*3600), secure)
|
||||
maxAge = cfg.Session.RememberTimeout * 60 // 分钟 → 秒
|
||||
} else {
|
||||
// session cookie:关浏览器即清除,但页面开启期间自动刷新生效
|
||||
setCookie(c, RefreshCookieName, refreshToken, 0, secure)
|
||||
setPlainCookie(c, RMCookieName, "1", 0, secure)
|
||||
maxAge = 0 // session cookie
|
||||
}
|
||||
|
||||
log.Printf("[SetSessionCookie] sid=%s rememberMe=%v maxAge=%ds secure=%v", sid[:16]+"...", rememberMe, maxAge, secure)
|
||||
c.SetCookie(SessionCookieName, sid, maxAge, "/", "", secure, true)
|
||||
}
|
||||
|
||||
// SetAccessCookie 仅刷新 access Cookie(refresh 续期调用)
|
||||
func SetAccessCookie(c *gin.Context, accessToken string, cfg *config.Config) {
|
||||
// ClearSessionCookie 清除会话 ID Cookie(logout 调用)
|
||||
func ClearSessionCookie(c *gin.Context, cfg *config.Config) {
|
||||
secure := cfg.Server.Mode != "debug"
|
||||
c.SetSameSite(http.SameSiteLaxMode)
|
||||
setCookie(c, CookieName, accessToken, cfg.JWT.AccessExpire*60, secure)
|
||||
c.SetCookie(SessionCookieName, "", -1, "/", "", secure, true)
|
||||
}
|
||||
|
||||
// ClearAuthCookies 清除所有认证 Cookie(logout 调用)
|
||||
func ClearAuthCookies(c *gin.Context, cfg *config.Config) {
|
||||
secure := cfg.Server.Mode != "debug"
|
||||
c.SetCookie(CookieName, "", -1, "/", "", secure, true)
|
||||
c.SetCookie(RefreshCookieName, "", -1, "/", "", secure, true)
|
||||
c.SetCookie(RMCookieName, "", -1, "/", "", secure, false)
|
||||
}
|
||||
|
||||
// setCookie 写入一个 HttpOnly Cookie
|
||||
func setCookie(c *gin.Context, name, value string, maxAge int, secure bool) {
|
||||
log.Printf("[setCookie] name=%s maxAge=%ds secure=%v", name, maxAge, secure)
|
||||
c.SetCookie(name, value, maxAge, "/", "", secure, true)
|
||||
}
|
||||
|
||||
// setPlainCookie 写入非 HttpOnly Cookie(JS 可读)
|
||||
func setPlainCookie(c *gin.Context, name, value string, maxAge int, secure bool) {
|
||||
c.SetCookie(name, value, maxAge, "/", "", secure, false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user