package authser import ( "crypto/rand" "encoding/hex" "fmt" "net/url" "strconv" "strings" "time" "videoweb/pub/common/log" "videoweb/pub/common/useragent" "github.com/gin-gonic/gin" ) const guestDevIDCookie = "guest_dev_id" // EnsureGuestOnEnter 页面进入时:无 token 则走游客登录并写 cookie。 // 对应上游 POST /api/app/auth/guest。成功后会把会话补进当前 Request,便于同一次 SSR 直接使用。 func EnsureGuestOnEnter(c *gin.Context) *LoginResp { if c == nil || c.Request == nil { return nil } // 已有会话则跳过 if useragent.TokenFromRequest(c.Request) != "" { return nil } devID := ensureGuestDevID(c) env, user, err := GuestLogin(c, GuestLoginReq{ DevID: devID, DeviceID: devID, Channel: guestChannel(c), }) if err != nil { log.Error("ensure guest on enter failed", log.E(err)) return nil } if env == nil || env.Code != 200 || user == nil || strings.TrimSpace(user.Token) == "" { tip := "" if env != nil { tip = firstNonEmptyTip(env.Tip, env.Msg) } log.Error("ensure guest on enter rejected", log.E(fmt.Errorf("code=%v tip=%s", envCode(env), tip))) return nil } SetAuthCookies(c, user) patchRequestCookie(c, "token", user.Token) return user } func dropRequestCookie(c *gin.Context, name string) { if c == nil || c.Request == nil || name == "" { return } var parts []string for _, ck := range c.Request.Cookies() { if ck == nil || ck.Name == name { continue } parts = append(parts, ck.Name+"="+ck.Value) } c.Request.Header.Set("Cookie", strings.Join(parts, "; ")) } func ensureGuestDevID(c *gin.Context) string { if id := cookieValue(c, guestDevIDCookie); id != "" { return id } id := newGuestDevID() secure := c.Request != nil && c.Request.TLS != nil setCookie(c, guestDevIDCookie, id, secure) patchRequestCookie(c, guestDevIDCookie, id) return id } func newGuestDevID() string { b := make([]byte, 8) if _, err := rand.Read(b); err != nil { now := time.Now() return fmt.Sprintf("%016X%d", uint64(now.UnixNano()), now.UnixMilli()) } return strings.ToUpper(hex.EncodeToString(b)) + strconv.FormatInt(time.Now().UnixMilli(), 10) } // guestChannel 为首屏服务端自动游客登录补渠道;cookie 用于复用前端首次归因结果。 func guestChannel(c *gin.Context) string { if c == nil || c.Request == nil { return "" } if channel := cookieValue(c, "channel"); channel != "" { return channel } for _, key := range []string{"dc", "ch", "pc"} { if channel := strings.TrimSpace(c.Query(key)); channel != "" { secure := c.Request.TLS != nil setCookie(c, "channel", channel, secure) patchRequestCookie(c, "channel", channel) return channel } } return "" } func patchRequestCookie(c *gin.Context, name, value string) { if c == nil || c.Request == nil || name == "" { return } escaped := url.PathEscape(value) var parts []string for _, ck := range c.Request.Cookies() { if ck == nil || ck.Name == name { continue } parts = append(parts, ck.Name+"="+ck.Value) } parts = append(parts, name+"="+escaped) c.Request.Header.Set("Cookie", strings.Join(parts, "; ")) } func envCode(env *APIEnvelope) int { if env == nil { return 0 } return env.Code } func firstNonEmptyTip(values ...string) string { for _, v := range values { if strings.TrimSpace(v) != "" { return v } } return "" }