90 lines
2.6 KiB
Go
90 lines
2.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/net/ghttp"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
// ctxKey 注入 request context 的键名。
|
|
type ctxKey string
|
|
|
|
const (
|
|
keyUid ctxKey = "authUid"
|
|
keyRole ctxKey = "authRole"
|
|
)
|
|
|
|
// GenerateToken 签发 HS256 JWT。
|
|
func GenerateToken(secret string, uid int64, role string, expireSeconds int) (string, error) {
|
|
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
"uid": uid,
|
|
"role": role,
|
|
"exp": time.Now().Add(time.Duration(expireSeconds) * time.Second).Unix(),
|
|
})
|
|
return claims.SignedString([]byte(secret))
|
|
}
|
|
|
|
// Secret 鉴权密钥(config.yml auth.secret),token 签发与解析共用入口。
|
|
func Secret(ctx context.Context) string {
|
|
return g.Cfg().MustGet(ctx, "auth.secret").String()
|
|
}
|
|
|
|
// ParseToken 校验并解析 token,返回 uid 与 role。
|
|
func ParseToken(secret, tokenString string) (uid int64, role string, err error) {
|
|
token, err := jwt.Parse(tokenString, func(t *jwt.Token) (any, error) {
|
|
return []byte(secret), nil
|
|
})
|
|
if err != nil || !token.Valid {
|
|
return 0, "", gerror.New("登录状态已失效,请重新登录")
|
|
}
|
|
claims, ok := token.Claims.(jwt.MapClaims)
|
|
if !ok {
|
|
return 0, "", gerror.New("登录状态解析失败")
|
|
}
|
|
return gconv.Int64(claims["uid"]), gconv.String(claims["role"]), nil
|
|
}
|
|
|
|
// Middleware 校验 Authorization: Bearer <token>,注入 uid/role 到请求上下文。
|
|
// requiredRole 为空表示任意登录角色;非空则额外校验角色匹配。
|
|
func Middleware(secret, requiredRole string) ghttp.HandlerFunc {
|
|
return func(r *ghttp.Request) {
|
|
tokenString := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
|
if tokenString == "" {
|
|
r.Response.WriteStatusExit(401, "未登录")
|
|
}
|
|
uid, role, err := ParseToken(secret, tokenString)
|
|
if err != nil {
|
|
r.Response.WriteStatusExit(401, err.Error())
|
|
}
|
|
if requiredRole != "" && role != requiredRole {
|
|
r.Response.WriteStatusExit(403, "无权限访问")
|
|
}
|
|
r.SetCtxVar(string(keyUid), uid)
|
|
r.SetCtxVar(string(keyRole), role)
|
|
r.Middleware.Next()
|
|
}
|
|
}
|
|
|
|
// GetUid 从请求上下文取登录 uid(未登录返回 0)。
|
|
func GetUid(ctx context.Context) int64 {
|
|
if req := g.RequestFromCtx(ctx); req != nil {
|
|
return gconv.Int64(req.GetCtxVar(string(keyUid)).Val())
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetRole 从请求上下文取登录角色。
|
|
func GetRole(ctx context.Context) string {
|
|
if req := g.RequestFromCtx(ctx); req != nil {
|
|
return gconv.String(req.GetCtxVar(string(keyRole)).Val())
|
|
}
|
|
return ""
|
|
}
|