update
This commit is contained in:
@ -3,6 +3,7 @@ package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"fashionapi/internal/pkg/jwt"
|
||||
@ -25,21 +26,43 @@ const bearerPrefix = "Bearer "
|
||||
// 令牌签发器由外部注入,避免像原实现那样在每个请求里重新加载一次配置。
|
||||
func Auth(manager *jwt.Manager) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
header := c.GetHeader("Authorization")
|
||||
if !strings.HasPrefix(header, bearerPrefix) {
|
||||
response.AbortError(c, http.StatusUnauthorized, "missing token")
|
||||
return
|
||||
}
|
||||
requireAuth(c, manager)
|
||||
}
|
||||
}
|
||||
|
||||
claims, err := manager.Parse(strings.TrimPrefix(header, bearerPrefix))
|
||||
if err != nil {
|
||||
response.AbortError(c, http.StatusUnauthorized, "invalid token")
|
||||
return
|
||||
}
|
||||
// requireAuth 校验 Bearer token 并把身份信息写入 Context;失败则 401 中断。
|
||||
func requireAuth(c *gin.Context, manager *jwt.Manager) {
|
||||
header := c.GetHeader("Authorization")
|
||||
if !strings.HasPrefix(header, bearerPrefix) {
|
||||
response.AbortError(c, http.StatusUnauthorized, "missing token")
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(ContextUserID, claims.UserID)
|
||||
c.Set(ContextUsername, claims.Username)
|
||||
c.Next()
|
||||
claims, err := manager.Parse(strings.TrimPrefix(header, bearerPrefix))
|
||||
if err != nil {
|
||||
response.AbortError(c, http.StatusUnauthorized, "invalid token")
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(ContextUserID, claims.UserID)
|
||||
c.Set(ContextUsername, claims.Username)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
// PublicFirstPageAuth 列表接口「首页公开、翻页需登录」鉴权中间件。
|
||||
//
|
||||
// 仅当分页参数 page 缺失或 <= 1 时放行(无需 token),其余页要求有效的
|
||||
// Authorization: Bearer <token>,否则返回 401。用于让列表首屏对游客/SEO 可见,
|
||||
// 同时保留翻页 / 批量枚举的登录门槛(防爬虫直接 dump 全量)。
|
||||
func PublicFirstPageAuth(manager *jwt.Manager) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if pageStr := c.Query("page"); pageStr != "" {
|
||||
if n, err := strconv.Atoi(pageStr); err == nil && n > 1 {
|
||||
requireAuth(c, manager) // 非首页:走完整 JWT 校验
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Next() // 首页(page 缺失或 <= 1)放行
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -47,6 +47,12 @@ func New(opt Options) *gin.Engine {
|
||||
// ClientSign 仅挂在「列表 / 品牌」两个批量查询接口上:
|
||||
// 这俩是爬虫批量 dump 的主要目标,要求前端 JS 签名可抬高抓取成本。
|
||||
public.Use(middleware.ClientSign(opt.Config.ClientSign))
|
||||
// 列表接口「首页公开、翻页需登录」:page=1(或缺省)无需 token 直接放行,
|
||||
// page>1 才要求 Authorization: Bearer <token>,否则 401。
|
||||
// 这样既让游客/SEO 看到首屏,又保留翻页 / 批量枚举的登录门槛(防爬虫 dump 全量)。
|
||||
// 注意:详情接口 /api/v1/public/*/:id 不在此组内,保持公开——详情页走 SSR
|
||||
// 服务端渲染、无用户 token,且分享链接/SEO 需可访问;列表隐藏即可防批量枚举。
|
||||
public.Use(middleware.PublicFirstPageAuth(opt.JWT))
|
||||
{
|
||||
// 走秀档案列表(按品牌过滤走 ?brand_id 查询参数)
|
||||
public.GET("/runway-looks", opt.Article.List)
|
||||
|
||||
Reference in New Issue
Block a user