使用Kratos进行代码的开发,对一些简单功能进行简单的记录。

JWT验证

拦截中间件以及验证JWT Token,过滤掉不验证的URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func NewWhiteListMatcher() selector.MatchFunc {
whiteList := make(map[string]struct{})
whiteList["/xxx.v1.xxxxService/Login"] = struct{}{}
return func(ctx context.Context, operation string) bool {
if _, ok := whiteList[operation]; ok {
return false
}
return true
}
}

http.Middleware(
selector.Server(
recovery.Recovery(),
ratelimit.Server(),
jwt.Server(
func(token *jwt4.Token) (interface{}, error) {
return []byte(auth.ApiKey), nil
},
jwt.WithSigningMethod(jwt4.SigningMethodHS256), // 设置加密算法
jwt.WithClaims(func() jwt4.Claims {
return &jwt4.MapClaims{}
}),
),
jwtverify.Server(c),
).Match(NewWhiteListMatcher()).Build(),
),

// jwtverify.Server(c) 用来拦截解析出来的jwt,以作验证
type JwtUser struct {
UID int64 `json:"uid"`
}

func Server(config *conf.Server) middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {

user := JwtUser{}
if claims, ok := jwt.FromContext(ctx); ok {
arr, _ := json.Marshal(claims)
json.Unmarshal(arr, &user)
}
if user.UID == 0 {
return nil, errors.Unauthorized("", "jwt get user failed")
}

return handler(ctx, req)
}
}
}
/////////////////////////////////////////////////////////////////

Login生成token:

1
2
3
4
5
6
7
8
9
// generate token
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"uid": user.Id,
"exp": time.Now().Unix() + 60*60*24,
})
token, err := claims.SignedString([]byte(uc.key))
if err != nil {
return nil, err
}

生成的token,前端放在HeaderAuthorization中。经过jwt中间件的解析,然后再经过我们写的中间件加上我们自己的验证方式,例如验证参数逻辑。

中间件的执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
http.Middleware(
middleware,
middleware2,
)

func sayHelloHandler(ctx http.Context) error {
......

http.SetOperation(ctx, "/helloworld.Greeter/SayHello")
h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {

fmt.Println("sayHelloHandler in")

return &helloworld.HelloReply{Message: req.(*helloworld.HelloRequest).Name}, nil
})
err := ctx.Returns(h(ctx, &in))

fmt.Println("sayHelloHandler out")

return err
}

//middleware in
//middleware 2 in
//sayHelloHandler in

//middleware 2 out
//middleware out
//sayHelloHandler out

// 处理函数不使用上述格式
func uploadFile(ctx http.Context) error {

fmt.Println("service uploadFile in")

return nil
}
// 使用此格式,将无法使用http.Middleware中间件,因为其没有调用ctx.Middleware,也就没法链接插件。
// 同理,其他HTTP框架如gin,echo等均需满足此格式。

综上,我们自己写的代码要使用Server Middleware,需要按照上面的格式进行编写。

另外还有很多插件形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
httpSrv := http.NewServer(
http.Address(":8001"),
http.Middleware(
// add service filter
serviceMiddleware,
serviceMiddleware2,
),
// add global filter
http.Filter(globalFilter, globalFilter2),
)
// register http hanlder to http server
helloworld.RegisterGreeterHTTPServer(httpSrv, s)

// add route filter
r := httpSrv.Route("/", routeFilter, routeFilter2)
// add path filter to custom route
r.GET("/hello/{name}", sayHelloHandler, pathFilter, pathFilter2)

执行顺序为:globalFilter(http) --> routeFilter(http) --> pathFilter(http) --> serviceFilter(service)