138 lines
3.3 KiB
Go
138 lines
3.3 KiB
Go
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 ""
|
|
}
|