go-kit 微服务 身份认证(JWT)

Jwt官网介绍

  • JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。

简介

开始

下载依赖

  • jwt库 go get github.com/dgrijalva/jwt-go

jwt编写

  • jwtSecret 生成token的密钥
  • CreateJwtToken 生成token
  • ParseToken 解析token
var jwtSecret = []byte("jwtSecret_v3")
const JWT_CONTEXT_KEY = "jwt_context_key"
type Token struct {
	Name string
	DcId int
	jwt.StandardClaims
}
func CreateJwtToken(name string, dcId int) (string, error) {
	var token Token
	token.StandardClaims = jwt.StandardClaims{
		Audience:  "",                                      // 受众群体
		ExpiresAt: time.Now().Add(30 * time.Second).Unix(), // 到期时间
		Id:        "",                                      // 编号
		IssuedAt:  time.Now().Unix(),                       // 签发时间
		Issuer:    "kit_v3",                                // 签发人
		NotBefore: time.Now().Unix(),                       // 生效时间
		Subject:   "login",                                 // 主题
	}
	token.Name = name
	token.DcId = dcId
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, token)
	return tokenClaims.SignedString(jwtSecret)
}
func ParseToken(token string) (jwt.MapClaims, error) {
	jwtToken, err := jwt.ParseWithClaims(token, jwt.MapClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return jwtSecret, nil
	})
	if err != nil || jwtToken == nil {
		return nil, err
	}
	claim, ok := jwtToken.Claims.(jwt.MapClaims)
	if ok && jwtToken.Valid {
		return claim, nil
	} else {
		return nil, nil
	}
}
  • 测试结果
    jwt_test.go:12: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MiwiZXhwIjoxNTc3OTUwOTQ5LCJpYXQiOjE1Nzc5NTA5MTksImlzcyI6ImtpdF92MyIsIm5iZiI6MTU3Nzk1MDkxOSwic3ViIjoibG9naW4ifQ.w3us4_qpzm_HDPVlwDRSqIfcTT34DpmzD5_X8Z_g-jQ
    jwt_test.go:17: map[DcId:2 Name:hwholiday exp:1.577950949e+09 iat:1.577950919e+09 iss:kit_v3 nbf:1.577950919e+09 sub:login]

Server层修改

添加Login方法

type Service interface {
	TestAdd(ctx context.Context, in Add) AddAck
	Login(ctx context.Context, in Login) (ack LoginAck, err error)
}
...省略...
func (s baseServer) Login(ctx context.Context, in Login) (ack LoginAck, err error) {
	s.logger.Debug(fmt.Sprint(ctx.Value(ContextReqUUid)), zap.Any("调用 v3_service Service", "Login 处理请求"))
	if in.Account != "hwholiday" || in.Password != "123456" {
		err = errors.New("用户信息错误")
		return
	}
	ack.Token, err = utils.CreateJwtToken(in.Account,1)
	s.logger.Debug(fmt.Sprint(ctx.Value(ContextReqUUid)), zap.Any("调用 v3_service Service", "Login 处理请求"), zap.Any("处理返回值", ack))
	return
}

Endpoint层修改

  • 添加jwt鉴权中间件
func AuthMiddleware(logger *zap.Logger) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			token := fmt.Sprint(ctx.Value(utils.JWT_CONTEXT_KEY))
			if token == "" {
				err = errors.New("请登录")
				logger.Debug(fmt.Sprint(ctx.Value(v3_service.ContextReqUUid)),zap.Any("[AuthMiddleware]","token == empty"), zap.Error(err))
				return "", err
			}
			jwtInfo, err := utils.ParseToken(token)
			if err != nil {
				logger.Debug(fmt.Sprint(ctx.Value(v3_service.ContextReqUUid)),zap.Any("[AuthMiddleware]","ParseToken"), zap.Error(err))
				return "", err
			}
			if v, ok := jwtInfo["Name"]; ok {
				ctx = context.WithValue(ctx, "name", v)
			}
			return next(ctx, request)
		}
	}
}
  • 对于需要鉴权的方法添加jwt鉴权中间件
var addEndPoint endpoint.Endpoint
{
	addEndPoint = MakeAddEndPoint(svc)
	addEndPoint = LoggingMiddleware(log)(addEndPoint)
	addEndPoint = AuthMiddleware(log)(addEndPoint)
}

Transport层修改

  • 将每个Http请求Header中的token传入Context中
httptransport.ServerBefore(func(ctx context.Context, request *http.Request) context.Context {
	UUID := uuid.NewV5(uuid.Must(uuid.NewV4()), "req_uuid").String()
	log.Debug("给请求添加uuid", zap.Any("UUID", UUID))
	ctx = context.WithValue(ctx, v3_service.ContextReqUUid, UUID)
	ctx = context.WithValue(ctx, utils.JWT_CONTEXT_KEY, request.Header.Get("Authorization"))
	ctx = context.WithValue(ctx, utils.JWT_CONTEXT_KEY, request.Header.Get("Authorization"))
	return ctx
}

激动人心的时刻来了,运行main方法

  • 登录获取Token
    • [Post 方法] http://127.0.0.1:8888/login 内容 {“account”:“hwholiday”,“password”:“123456”}
    • 运行日志
2020-01-02 15:55:13     DEBUG   v3_transport/transport.go:26    给请求添加uuid  {"UUID": "9fcc91ac-dd8d-53cb-8c9d-f06561d82c6f"}
2020-01-02 15:55:13     DEBUG   v3_transport/transport.go:29    把请求中的token发到Context中    {"Token": ""}
2020-01-02 15:55:13     DEBUG   v3_transport/transport.go:56    9fcc91ac-dd8d-53cb-8c9d-f06561d82c6f    {" 开始解析请求数据"unt":"hwholiday","password":"123456"}}
2020-01-02 15:55:13     DEBUG   v3_service/service.go:38        9fcc91ac-dd8d-53cb-8c9d-f06561d82c6f    {"调用 v3_service Seice": "Login 处理请求"}
2020-01-02 15:55:13     DEBUG   v3_service/service.go:44        9fcc91ac-dd8d-53cb-8c9d-f06561d82c6f    {"调用 v3_service Seice": "Login 处理请求", "处理返回值": {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MSwNTc3OTUxNzQzLCJpYXQiOjE1Nzc5NTE3MTMsImlzcyI6ImtpdF92MyIsIm5iZiI6MTU3Nzk1MTcxMywic3ViIjoibG9naW4ifQ.2s2YzKhMHSXRjzgO4yfHP7gHADeVwpsi9CyXPgAQjmQ"}}
2020-01-02 15:55:13     DEBUG   v3_service/middleware.go:37     9fcc91ac-dd8d-53cb-8c9d-f06561d82c6f    {"调用 Login logMiddwareServer": "Login", "req": {"account":"hwholiday","password":"123456"}, "res": {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MSwiZXhwIjoxNTc3OTUxNzQzLCJpYXQiOjE1Nzc5NTE3MTMsImlzcyI6ImtpdF92MyIsIm5iZiI6MTU3Nzk1MTcxMywic3ViIjoibG9naW4ifQ.2s2YzKhMHSXRjzgO4yfHP7gHADeVwpsi9CyXPgAQjmQ"}, "err": null}
2020-01-02 15:55:13     DEBUG   v3_endpoint/middleware.go:18    9fcc91ac-dd8d-53cb-8c9d-f06561d82c6f    {"调用 v3_endpoint LgingMiddleware": "处理完请求", "耗时毫秒": 0}
2020-01-02 15:55:13     DEBUG   v3_transport/transport.go:75    9fcc91ac-dd8d-53cb-8c9d-f06561d82c6f    {"请求结束封装返回值n":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MSwiZXhwIjoxNTc3OTUxNzQzLCJpYXQiOjE1Nzc5NTE3MTMsImlzcyI6ImtpdF92MyIsIm5iZiI6MTU3Nzk1MTcxMywic3ViIjoibG9naW4ifQ.2s2YzKhMHSXRjzgO4yfHP7gHADeVwpsi9CyXPgAQjmQ"}}
  • 调用sum方法
    • [Get 方法] 127.0.0.1:8888/sum?a=1&b=1 添加登录获取到的token放入Header里面(Header的key为Authorization)
    • Token没过期的运行日志
2020-01-02 15:58:13     DEBUG   v3_transport/transport.go:26    给请求添加uuid  {"UUID": "4e44ed1f-e424-5313-83e0-b68cf5cce6e1"}
2020-01-02 15:58:13     DEBUG   v3_transport/transport.go:29    把请求中的token发到Context中    {"Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MSwiZXhwIjoxNTc3OTUxOTA2LCJpYXQiOjE1Nzc5NTE4NzYsImlzcyI6ImtpdF92MyIsIm5iZiI6MTU3Nzk1MTg3Niwic3ViIjoibG9naW4ifQ.7kMgtVMAQaJJ92MeFPdO7CWnNwY6cumLjUuvZGAd-Z0"}
2020-01-02 15:58:13     DEBUG   v3_transport/transport.go:70    4e44ed1f-e424-5313-83e0-b68cf5cce6e1    {" 开始解析请求数据": {"a":1,"b":1}}
2020-01-02 15:58:13     DEBUG   v3_service/service.go:31        4e44ed1f-e424-5313-83e0-b68cf5cce6e1    {"调用 v3_service Service": "TestAdd 处理请求", "请求用户": "hwholiday"}
2020-01-02 15:58:13     DEBUG   v3_service/service.go:33        4e44ed1f-e424-5313-83e0-b68cf5cce6e1    {"调用 v3_service Service": "TestAdd 处理请求", "处理返回值": {"res":2}}
2020-01-02 15:58:13     DEBUG   v3_service/middleware.go:29     4e44ed1f-e424-5313-83e0-b68cf5cce6e1    {"调用 Login logMiddlewareServer": "TestAdd", "req": {"a":1,"b":1}, "res": {"res":2}}
2020-01-02 15:58:13     DEBUG   v3_endpoint/middleware.go:18    4e44ed1f-e424-5313-83e0-b68cf5cce6e1    {"调用 v3_endpoint LoggingMiddleware": "处理完请求", "耗时毫秒": 2}
2020-01-02 15:58:13     DEBUG   v3_transport/transport.go:75    4e44ed1f-e424-5313-83e0-b68cf5cce6e1    {"请求结束封装返回值": {"res":2}}
  
  • Token过期的运行日志
2020-01-02 15:59:16     DEBUG   v3_transport/transport.go:26    给请求添加uuid  {"UUID": "b017a114-9216-5f67-b9bb-a8e378a73828"}
2020-01-02 15:59:16     DEBUG   v3_transport/transport.go:29    把请求中的token发到Context中    {"Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MSwiZXhwIjoxNTc3OTUxOTA2LCJpYXQiOjE1Nzc5NTE4NzYsImlzcyI6ImtpdF92MyIsIm5iZiI6MTU3Nzk1MTg3Niwic3ViIjoibG9naW4ifQ.7kMgtVMAQaJJ92MeFPdO7CWnNwY6cumLjUuvZGAd-Z0"}
2020-01-02 15:59:16     DEBUG   v3_transport/transport.go:70    b017a114-9216-5f67-b9bb-a8e378a73828    {" 开始解析请求数据": {"a":1,"b":1}}
2020-01-02 15:59:16     DEBUG   v3_endpoint/middleware.go:35    b017a114-9216-5f67-b9bb-a8e378a73828    {"[AuthMiddleware]": "ParseToken", "error": "Token is expired"}
2020-01-02 15:59:16     WARN    v3_transport/transport.go:20    b017a114-9216-5f67-b9bb-a8e378a73828    {"error": "Token is expired"}

结语

  • 加入JWT机制,我们可以对接口实现只接受我们认证的用户访问
  • 欢迎添加QQ一起讨论

完整代码地址

联系 QQ: 3355168235