Go-kratos 框架商城微服务实战六

502 阅读2分钟

Go-kratos 框架商城微服务实战六

中间件-jwt鉴权

在使用的时候,遇到了一些问题,官网的信息也不是很明确,记录于次。

  • 首先参考示例写法(jwt鉴权+白名单)[github.com/go-kratos/b…]
  • 不难发现需要咱们处理的地方有以下几处
  1. 函数的白名单设置

    func NewWhiteListMatcher() selector.MatchFunc {
    
        whiteList := make(map[string]struct{})
        whiteList["/shop.interface.v1.ShopInterface/Login"] = struct{}{}
        whiteList["/shop.interface.v1.ShopInterface/Register"] = struct{}{}
        return func(ctx context.Context, operation string) bool {
            if _, ok := whiteList[operation]; ok {
                return false
            }
            return true
        }
    }
    
  2. NewHTTPServer的传入参数ac *conf.Auth, 这里涉及到依赖注入的过程, 咱们先忽略掉。

  3. 核心部分则是中间件的使用

    selector.Server(
    			jwt.Server(func(token *jwt2.Token) (interface{}, error) {
    				return []byte(ac.ApiKey), nil
    			}, jwt.WithSigningMethod(jwt2.SigningMethodHS256), jwt.WithClaims(func() jwt2.Claims {
    				return &jwt2.MapClaims{}
    			})),
    		).
    			Match(NewWhiteListMatcher()).
    			Build(),
    

    结合 selector 和 jwt 来做白名单过滤, 这里为了忽略掉依赖注入的影响, 可以改成如下内容。

    selector.Server(
    			jwt.Server(func(token *jwt2.Token) (interface{}, error) {
    				return []byte("testKey"), nil
    			}, jwt.WithSigningMethod(jwt2.SigningMethodHS256), jwt.WithClaims(func() jwt2.Claims {
    				return &jwt2.MapClaims{}
    			})),
    		).
    			Match(NewWhiteListMatcher()).
    			Build(),
    

白名单处理

踩坑的地方在于白名单的设置规则, 白名单的读取是通过读取\api目录下对应的proto文件进行的。

例如:在 `/api/user/v1/user.proto中, 有如下定义

service User {
	rpc CreateUser (CreateUserRequest) returns (CreateUserReply) {
        option (google.api.http) = {
            post: "/v1/users"
            body: "*"
        };
    }
}

那么想对/v1/users进行白名单设置, 需要这样设置

whiteList["/api.user.v1.User/CreateUser"] = struct{}{}
// package名/接口名

下面给出完整代码

package server

import (
	"context"
	v1 "kratos-shop/api/helloworld/v1"
	v2 "kratos-shop/api/shop/v1"
	"kratos-shop/app/shop/internal/conf"
	"kratos-shop/app/shop/internal/service"

	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
	"github.com/go-kratos/kratos/v2/middleware/logging"
	"github.com/go-kratos/kratos/v2/middleware/metadata"
	"github.com/go-kratos/kratos/v2/middleware/recovery"
	"github.com/go-kratos/kratos/v2/middleware/selector"
	"github.com/go-kratos/kratos/v2/middleware/validate"
	"github.com/go-kratos/kratos/v2/transport/http"
	jwtv5 "github.com/golang-jwt/jwt/v5"
	"github.com/gorilla/handlers"
)

// NewHTTPServer new an HTTP server.
func NewHTTPServer(c *conf.Server, ac *conf.Auth, s *service.ShopService, greeter *service.GreeterService, logger log.Logger) *http.Server {
	var opts = []http.ServerOption{
		http.Middleware(
			recovery.Recovery(),
			validate.Validator(),
			selector.Server(
				jwt.Server(func(token *jwtv5.Token) (interface{}, error) {
					return []byte(ac.JwtKey), nil
				}),
			).Match(NewWhiteListMatcher()).Build(), // 设置白名单
			metadata.Server(),
			logging.Server(logger),
		),
		http.Filter(handlers.CORS( // 浏览器跨域
			handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}),
			handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"}),
			handlers.AllowedOrigins([]string{"*"}),
		)),
	}
	if c.Http.Network != "" {
		opts = append(opts, http.Network(c.Http.Network))
	}
	if c.Http.Addr != "" {
		opts = append(opts, http.Address(c.Http.Addr))
	}
	if c.Http.Timeout != nil {
		opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
	}
	srv := http.NewServer(opts...)
	v1.RegisterGreeterHTTPServer(srv, greeter)
	v2.RegisterShopHTTPServer(srv, s)
	return srv
}

// NewWhiteListMatcher 设置白名单,不需要 token 验证的接口
func NewWhiteListMatcher() selector.MatchFunc {
	whiteList := make(map[string]struct{})
	whiteList["/shop.shop.v1.Shop/Captcha"] = struct{}{}
	whiteList["/shop.shop.v1.Shop/Login"] = struct{}{}
	whiteList["/shop.shop.v1.Shop/Register"] = struct{}{}
	return func(ctx context.Context, operation string) bool {
		if _, ok := whiteList[operation]; ok {
			return false
		}
		return true
	}
}