03:开发自定义中间件
Kratos 内置了一系列的 middleware(中间件)用于处理 logging、 metrics 等通用场景。您也可以通过实现 Middleware 接口,开发自定义 middleware,进行通用的业务处理,比如用户登录鉴权等。
1、Kratos 中间件生效顺序:
一个请求进入时的处理顺序为 Middleware 注册的顺序,而响应返回的处理顺序为注册顺序的倒序
┌───────────────────┐
│MIDDLEWARE 1 │
│ ┌────────────────┐│
│ │MIDDLEWARE 2 ││
│ │ ┌─────────────┐││
│ │ │MIDDLEWARE 3 │││
│ │ │ ┌─────────┐ │││
REQUEST │ │ │ │ YOUR │ │││ RESPONSE
──────┼─┼─┼─▷ HANDLER ○─┼┼┼───▷
│ │ │ └─────────┘ │││
│ │ └─────────────┘││
│ └────────────────┘│
└───────────────────┘
自定义中间件
一、实现一个Token验证中间件,需要实现接口:Middleware;下面实现通过JWT Token 验证登录状态的中间件; 1、使用 tr, ok := transport.FromServerContext(ctx) 获得 Transporter 实例 2、从 Transporter 对象获取 Header中的 token,并进行验证; 3、通过 selector.Server 设置验证白名单; 最终实现代码如下:
package server
import (
"context"
"strings"
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
"github.com/go-kratos/kratos/v2/middleware/selector"
"github.com/go-kratos/kratos/v2/transport"
v1 "sys_api/api/gen/user/v1"
"sys_api/internal/conf"
"sys_api/pkg/app"
)
var ERROR_UNAUTH = v1.ErrorNotLoggedIn("认证失败")
func authMiddleware(conf *conf.Data_Jwt) middleware.Middleware {
return selector.Server(
func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
if tr, ok := transport.FromServerContext(ctx); ok {
token := tr.RequestHeader().Get("Authorization")
token = strings.Replace(token, "Bearer", "", 1)
if token == "" {
err = ERROR_UNAUTH
return
}
claims, parseErr := app.ParseToken(conf, token)
if parseErr != nil || claims == nil {
return nil, ERROR_UNAUTH.WithCause(parseErr)
}
nctx := jwt.NewContext(ctx, claims)
reply, err = handler(nctx, req)
return
}
err = jwt.ErrMissingJwtToken
return
}
},
).Match(NewWhiteListMatcher()).Build()
}
// NewWhiteListMatcher 创建jwt白名单
func NewWhiteListMatcher() selector.MatchFunc {
whiteList := make(map[string]bool)
//忽略登录验证接口
whiteList[v1.UserService_Login_FullMethodName] = true
return func(ctx context.Context, operation string) bool {
if _, ok := whiteList[operation]; ok {
return false
}
return true
}
}
二、实现一个参数签名验证中间件:
if tr, ok := transport.FromServerContext(ctx); ok {
if ht, ok := tr.(*http.Transport); ok {
httpReq := ht.Request() // 返回 *http.Request
//TODO 通过 *http.Request 获取到参训进行签名验证
}
reply, err = handler(ctx, req)
return
}
使用中间件
在 NewGRPCServer 和 NewHTTPServer 中通过 ServerOption 进行注册。
var opts = []http.ServerOption{
http.Middleware(
tracing.Server(),
logging.Server(logger),
validate.Validator(),
authMiddleware(jwtConf),
recovery.Recovery(),
),
}