fix(file): 修复商品详情截图与提现打款凭证等私有图片 403 权限问题

- 放宽公开商品截图 key 提取规则,支持任意合规 object key 转换为免鉴权公开端点
- 补充后台截图上传 listing_screenshot 与 operating-expense 场景识别
- dependencies 授权器支持已审核上架商品截图兜底放行、提现人打款凭证鉴权及变体后缀匹配
- /api/files 路由组支持用户与管理员双重鉴权 (AuthUserOrAdmin)
- 前端收款账号凭证与提现详情凭证统一升级为 AuthImage 组件并支持大图预览
This commit is contained in:
yml2213
2026-09-16 20:33:09 +08:00
parent 64a4e5bb16
commit 69774458b0
13 changed files with 215 additions and 27 deletions
+67
View File
@@ -96,6 +96,73 @@ func Auth(jwtManager *auth.JWTManager, validate UserTokenValidatorFunc) gin.Hand
}
}
// AuthUserOrAdmin 允许普通用户或管理员访问共享资源(如私有文件流)。
func AuthUserOrAdmin(jwtManager *auth.JWTManager, userValidate UserTokenValidatorFunc, adminValidate AdminTokenValidatorFunc) gin.HandlerFunc {
return func(c *gin.Context) {
// 1. 优先尝试管理员鉴权(Header Bearer 或 Admin Cookie)
if tokenText, tokenSource := extractAdminToken(c); tokenText != "" {
if claims, err := jwtManager.ParseSubject(tokenText, "access", "admin"); err == nil {
username := claims.Phone
passwordMustChange := false
validAdmin := true
if adminValidate != nil {
tokenContext, err := adminValidate(c.Request.Context(), claims.UserID, claims.TokenVersion)
if err != nil {
RecordAdminAuthFailure(c, "invalid", tokenSource, 0, 0)
validAdmin = false
} else {
username = tokenContext.Username
passwordMustChange = tokenContext.PasswordMustChange
}
}
if validAdmin {
c.Set(ContextAdminID, claims.UserID)
c.Request = c.Request.WithContext(logging.WithAdminID(c.Request.Context(), claims.UserID))
c.Set(ContextUsername, username)
c.Set(ContextPasswordMustChange, passwordMustChange)
c.Next()
return
}
}
}
// 2. 尝试普通用户鉴权(Header Bearer 或 User Cookie)
tokenText := extractBearerToken(c)
if tokenText == "" {
if cookieToken, err := c.Cookie(UserAccessCookieName); err == nil {
tokenText = strings.TrimSpace(cookieToken)
}
}
if tokenText == "" {
response.Unauthorized(c, "缺少访问令牌")
c.Abort()
return
}
claims, err := jwtManager.ParseSubject(tokenText, "access", "user")
if err != nil {
response.Unauthorized(c, "访问令牌无效或已过期")
c.Abort()
return
}
if userValidate != nil {
if err := userValidate(c.Request.Context(), claims.UserID, claims.TokenVersion); err != nil {
if errors.Is(err, auth.ErrDependencyUnavailable) {
response.ServiceUnavailable(c, "用户认证服务暂时不可用")
} else {
response.Unauthorized(c, "登录状态已失效,请重新登录")
}
c.Abort()
return
}
}
c.Set(ContextUserID, claims.UserID)
c.Set(ContextPhone, claims.Phone)
c.Next()
}
}
func AdminAuth(jwtManager *auth.JWTManager, validate AdminTokenValidatorFunc) gin.HandlerFunc {
return func(c *gin.Context) {
tokenText, tokenSource := extractAdminToken(c)