EasyAudioEncode/pkg/web/jwt.go
2025-12-25 17:01:46 +08:00

142 lines
3.8 KiB
Go

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
}