package web import ( "fmt" "log/slog" "net/http" "strings" "git.lnton.com/lnton/pkg/reason" "git.lnton.com/lnton/pkg/web" "github.com/gin-gonic/gin" ) const ( uid = "uid" token = "token" username = "username" groupLevel = "group_level" level = "level" role = "role" ) // AuthMiddleware 鉴权 func AuthMiddleware(secret string, authAddr string, /*第三方鉴权地址*/ failedRedirect string, /*鉴权失败后重定向地址*/ ) gin.HandlerFunc { // var errResp = gin.H{ // "msg": "身份验证失败", // } return func(c *gin.Context) { // 创建不进行鉴权 // err := systemAuth(c, secret) // if err == nil { // c.Next() // return // } // slog.DebugContext(c.Request.Context(), "systemAuth鉴权失败", "err", err) // 使用第三方鉴权 if authAddr != "" { err := AuthenticateWithOAuth(c, authAddr) if err == nil { c.Next() return } slog.DebugContext(c.Request.Context(), "AuthenticateWithOAuth鉴权失败", "err", err) } // 鉴权失败进行重定向 if failedRedirect != "" { c.Writer.Header().Set("X-Redirect", failedRedirect) } web.AbortWithStatusJSON(c, reason.ErrUnauthorizedToken.SetMsg("身份验证失败")) } } func systemAuth(c *gin.Context, secret string) error { auth := c.Request.Header.Get(" Authorization") const prefix = "Bearer " if len(auth) <= len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) { return reason.ErrUnauthorizedToken.SetMsg("鉴权失败") } claims, err := web.ParseToken(auth[len(prefix):], secret) if err != nil { return reason.ErrUnauthorizedToken.SetMsg("身份已过期,请重新登录") } if err := claims.Valid(); err != nil { return reason.ErrUnauthorizedToken.SetMsg("身份已过期,请重新登录") } c.Set(uid, claims.UID) c.Set(username, claims.Username) c.Set(token, auth) c.Set(groupLevel, claims.GroupLevel) return nil } // AuthenticateWithOAuth 第三方鉴权逻辑 func AuthenticateWithOAuth(c *gin.Context, authAddr string) error { req, err := http.NewRequest(http.MethodPost, authAddr, nil) if err != nil { // c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create request"}) return reason.ErrBadRequest.SetMsg("failed to create request") } // 转发请求头 for key, values := range c.Request.Header { // 如果包含 origin 头,跳过 if strings.EqualFold(strings.ToLower(key), "origin") { continue } for _, value := range values { req.Header.Add(key, value) } } clientIP := c.ClientIP() // 获取客户端 IP 地址 // 获取现有的 X-Forwarded-For 头 existingIPs := c.Request.Header.Get("X-Forwarded-For") if existingIPs == "" { // 如果没有 X-Forwarded-For 头,设置客户端 IP req.Header.Set("X-Forwarded-For", clientIP) } else { // 如果已经存在 X-Forwarded-For 头,追加客户端 IP req.Header.Set("X-Forwarded-For", fmt.Sprintf("%s, %s", existingIPs, clientIP)) } // 发送请求 client := &http.Client{} // TODO:超时控制 resp, err := client.Do(req) if err != nil { return reason.ErrBadRequest.SetMsg("request to backend failed") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return reason.ErrUnauthorizedToken.SetMsg("身份验证失败") } // 读取响应 // responseBody, err := ioutil.ReadAll(resp.Body) // if err != nil { // return reason.ErrBadRequest.SetMsg("failed to read response body") // } // respStruct := struct { // Code int `json:"code"` // Msg string `json:"msg"` // }{} // if err := json.Unmarshal(responseBody, &respStruct); err != nil || respStruct.Code != 200 { // return reason.ErrUnauthorizedToken.SetMsg("身份验证失败") // } c.Set(uid, 224) c.Set("groupID", 111) c.Set(username, "api_admin") c.Set(role, "admin") c.Set(groupLevel, 1) c.Set(level, 1) c.Set(web.KeyRoleID, 1) // c.Set(token, auth) return nil }