feat: 家长 token 鉴权中间件与路由分组
This commit is contained in:
@@ -1,10 +1,23 @@
|
||||
package controller
|
||||
|
||||
import "github.com/gogf/gf/v2/net/ghttp"
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
|
||||
"36wisdom/common/auth"
|
||||
)
|
||||
|
||||
// Register 注册全部路由分组;各业务控制器在对应任务中挂载。
|
||||
func Register(s *ghttp.Server) {
|
||||
s.BindHandler("GET:/ping", func(r *ghttp.Request) {
|
||||
r.Response.Write("pong")
|
||||
})
|
||||
|
||||
secret := g.Cfg().MustGet(gctx.New(), "auth.secret").String()
|
||||
|
||||
// 前台:家长鉴权组(公开注册/登录接口除外),业务控制器按模块挂载
|
||||
s.Group("/api", func(g *ghttp.RouterGroup) {
|
||||
g.Middleware(auth.Middleware(secret, ""))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"36wisdom/biz/consts"
|
||||
"36wisdom/common/auth"
|
||||
)
|
||||
|
||||
// authSvc 鉴权服务:token 签发与解析,密钥统一来自配置 auth.secret。
|
||||
type authSvc struct{}
|
||||
|
||||
var Auth = &authSvc{}
|
||||
|
||||
func (s *authSvc) GenerateToken(ctx context.Context, uid int64, role string) (string, error) {
|
||||
return auth.GenerateToken(secret(ctx), uid, role, consts.AuthExpireSeconds)
|
||||
}
|
||||
|
||||
func (s *authSvc) ParseToken(ctx context.Context, tokenString string) (int64, string, error) {
|
||||
return auth.ParseToken(secret(ctx), tokenString)
|
||||
}
|
||||
|
||||
func secret(ctx context.Context) string {
|
||||
return g.Cfg().MustGet(ctx, "auth.secret").String()
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
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))
|
||||
}
|
||||
|
||||
// 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 ""
|
||||
}
|
||||
@@ -12,3 +12,6 @@ database:
|
||||
maxSize: 10000
|
||||
pool:
|
||||
defaultSize: 10
|
||||
auth:
|
||||
secret: "36wisdom-dev-secret-change-in-prod"
|
||||
expire: 604800
|
||||
|
||||
Reference in New Issue
Block a user