142 lines
3.8 KiB
Go
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
|
|
}
|