// Package auth 管理端 session:与 next-project lib/admin-auth.ts 的 token 格式兼容(exp.signature) package auth import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "net/http" "strconv" "strings" "time" ) const ( adminCookieName = "admin_session" maxAgeSec = 7 * 24 * 3600 // 7 天 ) // CreateAdminToken 生成签名 token,格式与 next 一致:exp.base64url(hmac_sha256(exp)) func CreateAdminToken(secret string) string { exp := time.Now().Unix() + maxAgeSec payload := strconv.FormatInt(exp, 10) mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(payload)) sig := base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) return payload + "." + sig } // VerifyAdminToken 校验 token:解析 exp、验签、验过期 func VerifyAdminToken(token, secret string) bool { if token == "" || secret == "" { return false } dot := strings.Index(token, ".") if dot <= 0 { return false } payload := token[:dot] sig := token[dot+1:] exp, err := strconv.ParseInt(payload, 10, 64) if err != nil || exp < time.Now().Unix() { return false } mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(payload)) expected := base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(sig), []byte(expected)) } // AdminCookieName 返回 Cookie 名 func AdminCookieName() string { return adminCookieName } // MaxAgeSec 返回 session 有效秒数 func MaxAgeSec() int { return maxAgeSec } // SetCookieHeaderValue 返回完整的 Set-Cookie 头内容(含 SameSite=None; Secure,供跨站时携带 Cookie) func SetCookieHeaderValue(token string, maxAge int) string { if maxAge <= 0 { return adminCookieName + "=; Path=/; Max-Age=0; HttpOnly; SameSite=None; Secure" } return adminCookieName + "=" + token + "; Path=/; Max-Age=" + strconv.Itoa(maxAge) + "; HttpOnly; SameSite=None; Secure" } // GetAdminTokenFromRequest 从请求 Cookie 中读取 admin_session func GetAdminTokenFromRequest(r *http.Request) string { c, err := r.Cookie(adminCookieName) if err != nil || c == nil { return "" } return strings.TrimSpace(c.Value) }