|
|
|
|
// Copyright (C) 2024 veypi <i@veypi.com>
|
|
|
|
|
// 2025-03-04 16:08:06
|
|
|
|
|
// Distributed under terms of the MIT license.
|
|
|
|
|
|
|
|
|
|
package auth
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/veypi/vbase/libs/cache"
|
|
|
|
|
"github.com/veypi/vbase/libs/jwt"
|
|
|
|
|
"github.com/veypi/vigo"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AuthMiddleware 统一认证中间件
|
|
|
|
|
// 仅处理 JWT 认证,设置 CtxKeyUserID
|
|
|
|
|
// 组织信息的加载需按需调用 Auth.LoadOrg(x)
|
|
|
|
|
func AuthMiddleware() func(*vigo.X) error {
|
|
|
|
|
return func(x *vigo.X) error {
|
|
|
|
|
// === 1. JWT 认证部分 ===
|
|
|
|
|
tokenString := extractToken(x)
|
|
|
|
|
if tokenString == "" {
|
|
|
|
|
return vigo.ErrUnauthorized.WithString("missing token")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解析token
|
|
|
|
|
claims, err := jwt.ParseToken(tokenString)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == jwt.ErrExpiredToken {
|
|
|
|
|
return vigo.ErrTokenExpired
|
|
|
|
|
}
|
|
|
|
|
return vigo.ErrTokenInvalid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查token是否在黑名单中
|
|
|
|
|
if cache.IsEnabled() {
|
|
|
|
|
blacklisted, _ := cache.IsTokenBlacklisted(claims.ID)
|
|
|
|
|
if blacklisted {
|
|
|
|
|
return vigo.ErrUnauthorized.WithString("token has been revoked")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将用户信息存入上下文
|
|
|
|
|
x.Set(CtxKeyUserID, claims.UserID)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func extractToken(x *vigo.X) string {
|
|
|
|
|
auth := x.Request.Header.Get("Authorization")
|
|
|
|
|
if auth != "" {
|
|
|
|
|
if len(auth) > 7 && strings.HasPrefix(auth, "Bearer ") {
|
|
|
|
|
return auth[7:]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return x.Request.URL.Query().Get("access_token")
|
|
|
|
|
}
|