u
This commit is contained in:
1 parent
8408cf779f
commit
25a9e41025
58 files changed
+2079
-6308
No files matched your search
@@ -0,0 +1,67 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"cn/oneao/base-go/utils/jwtx"
|
||||
"cn/oneao/base-go/utils/pathx"
|
||||
"cn/oneao/base-go/utils/response"
|
||||
"context"
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
)
|
||||
|
||||
// 私有 key,防止冲突
|
||||
type ctxKeyUserID struct{}
|
||||
|
||||
// Auth 中间件,直接在中间件里过滤白名单路径
|
||||
func Auth() app.HandlerFunc {
|
||||
// 白名单路径,直接写死
|
||||
whitelistPaths := []string{
|
||||
"/auth/login",
|
||||
"/auth/register",
|
||||
"/auth/refreshToken",
|
||||
}
|
||||
|
||||
return func(ctx context.Context, c *app.RequestContext) {
|
||||
path := string(c.Request.URI().Path())
|
||||
|
||||
// 使用工具函数判断是否在白名单
|
||||
if pathx.MatchPath(path, whitelistPaths) {
|
||||
c.Next(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取 token
|
||||
token := string(c.GetHeader("token"))
|
||||
if token == "" {
|
||||
response.Fail(c).Code(response.HttpCode.Unauthorized).Send()
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
verifyToken := jwtx.VerifyToken(token)
|
||||
|
||||
if verifyToken.IsValid {
|
||||
// 设置 userID 到 context
|
||||
ctx = setUserID(ctx, verifyToken.Claims.UserID)
|
||||
c.Next(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// token 无效
|
||||
response.Fail(c).Code(response.HttpCode.RefreshToken).Send()
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserID 从 context 中获取 user_id
|
||||
func GetUserID(ctx context.Context) int64 {
|
||||
userID, ok := ctx.Value(ctxKeyUserID{}).(int64)
|
||||
if !ok {
|
||||
return 0 // 或者返回 error,根据业务需求
|
||||
}
|
||||
return userID
|
||||
}
|
||||
|
||||
// 内部中间件设置 user_id
|
||||
func setUserID(ctx context.Context, userID int64) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyUserID{}, userID)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"cn/oneao/base-go/logx"
|
||||
"cn/oneao/base-go/utils/response"
|
||||
"context"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ErrorInfo 封装错误信息和调用栈
|
||||
type ErrorInfo struct {
|
||||
Message string // 错误信息
|
||||
FuncName string // 出错函数
|
||||
File string // 文件路径
|
||||
Line string // 行号
|
||||
Stack string // 完整堆栈信息
|
||||
}
|
||||
|
||||
// Error 中间件
|
||||
func Error() app.HandlerFunc {
|
||||
return func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Next(ctx)
|
||||
|
||||
if e := c.Errors.Last(); e != nil && e.Err != nil {
|
||||
info := ParseError(e.Err)
|
||||
|
||||
logx.CtxError(ctx, "系统异常",
|
||||
zap.String("Message", info.Message),
|
||||
zap.String("File", info.File),
|
||||
zap.String("Line", info.Line),
|
||||
zap.String("Func", info.FuncName),
|
||||
zap.String("Stack", info.Stack),
|
||||
)
|
||||
|
||||
// 返回给前端
|
||||
response.Fail(c).Message("系统异常,请联系管理员!").Send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ParseError 解析 error 为 ErrorInfo
|
||||
func ParseError(err error) *ErrorInfo {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
info := &ErrorInfo{
|
||||
Message: err.Error(),
|
||||
Stack: string(debug.Stack()), // 默认 runtime stack
|
||||
}
|
||||
|
||||
// 尝试解析 pkg/errors stack
|
||||
type stackTracer interface {
|
||||
StackTrace() errors.StackTrace
|
||||
}
|
||||
|
||||
if errWithStack, ok := err.(stackTracer); ok {
|
||||
st := errWithStack.StackTrace()
|
||||
var funcName, fileLine string
|
||||
for _, f := range st {
|
||||
line := fmt.Sprintf("%+v", f)
|
||||
// 取 biz/ 目录下第一个函数作为业务函数
|
||||
if strings.Contains(line, "/biz/") {
|
||||
parts := strings.Split(line, "\n")
|
||||
if len(parts) >= 2 {
|
||||
funcName = strings.TrimSpace(parts[0])
|
||||
fileLine = strings.TrimSpace(parts[1])
|
||||
} else {
|
||||
funcName = strings.TrimSpace(line)
|
||||
fileLine = strings.TrimSpace(line)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
info.FuncName = funcName
|
||||
// 分离文件和行号
|
||||
if idx := strings.LastIndex(fileLine, ":"); idx > 0 {
|
||||
info.File = fileLine[:idx]
|
||||
info.Line = fileLine[idx+1:]
|
||||
} else {
|
||||
info.File = fileLine
|
||||
info.Line = ""
|
||||
}
|
||||
|
||||
// 完整堆栈
|
||||
stackStr := ""
|
||||
for _, f := range st {
|
||||
stackStr += fmt.Sprintf("%+v\n", f)
|
||||
}
|
||||
info.Stack = stackStr
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/hertz-contrib/jwt"
|
||||
)
|
||||
|
||||
var identityKey = "UserId"
|
||||
|
||||
type loginForm struct {
|
||||
Account string `form:"username,required" json:"username,required"`
|
||||
Password string `form:"password,required" json:"password,required"`
|
||||
Type int `form:"type,required" json:"type,required"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
UserId int64
|
||||
}
|
||||
|
||||
var hertzJWTMiddleware *jwt.HertzJWTMiddleware
|
||||
|
||||
func init() {
|
||||
middleware, err := jwt.New(&jwt.HertzJWTMiddleware{
|
||||
Realm: "test zone",
|
||||
Key: []byte("secret key"),
|
||||
Timeout: 30 * time.Minute,
|
||||
MaxRefresh: 30 * 24 * time.Hour,
|
||||
IdentityKey: identityKey,
|
||||
// Token负载信息
|
||||
PayloadFunc: func(data interface{}) jwt.MapClaims {
|
||||
if v, ok := data.(*User); ok {
|
||||
return jwt.MapClaims{
|
||||
identityKey: v.UserId,
|
||||
}
|
||||
}
|
||||
return jwt.MapClaims{}
|
||||
},
|
||||
// 解析Token取出用户信息
|
||||
IdentityHandler: func(ctx context.Context, c *app.RequestContext) interface{} {
|
||||
claims := jwt.ExtractClaims(ctx, c)
|
||||
return &User{
|
||||
UserId: claims[identityKey].(int64),
|
||||
}
|
||||
},
|
||||
// 登录
|
||||
Authenticator: func(ctx context.Context, c *app.RequestContext) (interface{}, error) {
|
||||
c.Request.RequestURI()
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
},
|
||||
// 权限控制
|
||||
Authorizator: func(data interface{}, ctx context.Context, c *app.RequestContext) bool {
|
||||
return true
|
||||
},
|
||||
// 未验证处理
|
||||
Unauthorized: func(ctx context.Context, c *app.RequestContext, code int, message string) {
|
||||
c.JSON(code, map[string]interface{}{
|
||||
"code": code,
|
||||
"message": message,
|
||||
})
|
||||
},
|
||||
// 登录响应
|
||||
LoginResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
|
||||
c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"code": http.StatusOK,
|
||||
"token": token,
|
||||
"expire": expire.Format(time.RFC3339),
|
||||
})
|
||||
},
|
||||
// 登出响应
|
||||
LogoutResponse: func(ctx context.Context, c *app.RequestContext, code int) {
|
||||
c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"code": http.StatusOK,
|
||||
})
|
||||
},
|
||||
// 刷新Token
|
||||
RefreshResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) {
|
||||
c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"code": http.StatusOK,
|
||||
"token": token,
|
||||
"expire": expire.Format(time.RFC3339),
|
||||
})
|
||||
},
|
||||
TokenLookup: "header: Authorization",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("JWT Error:" + err.Error())
|
||||
}
|
||||
|
||||
hertzJWTMiddleware = middleware
|
||||
}
|
||||
|
||||
func JWT() *jwt.HertzJWTMiddleware {
|
||||
return hertzJWTMiddleware
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"cn/oneao/base-go/logx"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/hertz-contrib/requestid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Log() app.HandlerFunc {
|
||||
return func(c context.Context, ctx *app.RequestContext) {
|
||||
reqId := requestid.Get(ctx)
|
||||
|
||||
c = context.WithValue(c, "RequestID", reqId)
|
||||
|
||||
start := time.Now() // 记录开始时间
|
||||
|
||||
// 执行后续处理
|
||||
ctx.Next(c)
|
||||
|
||||
// 计算耗时
|
||||
duration := time.Since(start).Milliseconds()
|
||||
|
||||
logx.CtxInfo(c, "请求完成",
|
||||
zap.String("IP", ctx.ClientIP()),
|
||||
zap.String("Method", string(ctx.Method())),
|
||||
zap.String("Path", string(ctx.Path())),
|
||||
zap.Int("Status", ctx.GetResponse().StatusCode()),
|
||||
zap.Int64("Duration", duration),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user