前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go-kit 微服务 整合jwt实现授权认证

go-kit 微服务 整合jwt实现授权认证

原创
作者头像
Johns
修改2022-06-30 10:32:00
1.2K0
修改2022-06-30 10:32:00
举报
文章被收录于专栏:代码工具

什么是JWT?

Json web token (JWT) 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。 JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

JWT包括了三部分:header, payload,signature。

token
token
代码语言:javascript
复制
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJBY2NvdW50IjoiZTY5YTRmNjU5ZjZiZWM5NWE0MGRhNmUyMDc1OGUyOTciL
CJleHAiOjE2MTUyNTczMjcsImlhdCI6MTYxNTI1NzI5NywiaXNzIjoiYXBwX3
YxLjAuMSIsIm5iZiI6MTYxNTI1NzI5Nywic3ViIjoibG9naW4ifQ.
Vvoipb7i-BcoNjjbvnrVA2AXKK1JsGd19rZnUVGLzfg

  • head部分
代码语言:txt
复制
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
代码语言:txt
复制
base64解密后内容:     {"alg":"HS256","typ":"JWT"}
  • payload 部分
代码语言:txt
复制
eyJBY2NvdW50IjoiZTY5YTRmNjU5ZjZiZWM5NWE0MGRhNmUyMDc1OGUyOTciL
CJleHAiOjE2MTUyNTczMjcsImlhdCI6MTYxNTI1NzI5NywiaXNzIjoiYXBwX3
YxLjAuMSIsIm5iZiI6MTYxNTI1NzI5Nywic3ViIjoibG9naW4ifQ
代码语言:txt
复制
base64解密后内容:
{   "Account":"e69a4f659f6bec95a40da6e20758e297",
    "exp":1615257327,
    "iat":1615257297,
    "iss":"app_v1.0.1",
    "nbf":1615257297,
    "sub":"login"
}
  • signature 部分
代码语言:txt
复制
 Vvoipb7i-BcoNjjbvnrVA2AXKK1JsGd19rZnUVGLzfg

生成规则;

base64(head)+ '.'+base64(payload)+ 服务端自定义的secret串,通过header中声明的加密方式进行加盐secret组合加密

使用

  • 简单封装
代码语言:txt
复制
package utils

import (
   "crypto/md5"
   "encoding/hex"
   jwt "github.com/dgrijalva/jwt-go"
   "time"
)

var jwtSecret = []byte("testV3")
var md5Salt = []byte("salt001")

type Token struct {
   Account string
   jwt.StandardClaims
}

func CreateJwtToken(account string) (string, error) {
   var token Token
   token.StandardClaims = jwt.StandardClaims{
      Audience:  "",                                      // 受众群体
      ExpiresAt: time.Now().Add(30 * time.Second).Unix(), // 到期时间
      Id:        "",                                      // 编号
      IssuedAt:  time.Now().Unix(),                       // 签发时间
      Issuer:    "app_v1.0.1",                            // 签发人
      NotBefore: time.Now().Unix(),                       // 生效时间
      Subject:   "login",                                 // 主题
   }

   h := md5.New()
   h.Write([]byte(account))
   h.Write(md5Salt)
   cipherStr := h.Sum(nil)
   accountAuthStr := hex.EncodeToString(cipherStr)

   token.Account = accountAuthStr
   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
   }
}
  • 新增登录服务,登录成功返回token给客户端

service.go

代码语言:txt
复制
func (s baseService) Login(ctx context.Context, in LoginRequest) (resp LoginResponse, err error) {
   reqId := ctx.Value(utils.BaseRequestId).(string)
   log.GetLogger().Info("service layer: do Login handler, reqId=" + reqId)
   if in.Account != "ggr" || in.PassWord != "12345" {
      return resp, err
   }
   resp.Token, err = utils.CreateJwtToken(in.Account)
   if err != nil {
      return resp, err
   }
   return
}

endpoint.go

代码语言:txt
复制
package endpoint

import (
   "context"
   "github.com/go-kit/kit/endpoint"
   "go-kit-microservice/internal/pkg/log"
   "go-kit-microservice/internal/pkg/utils"
   "go-kit-microservice/internal/service"
)

type EndPoints struct {
   MultiplyEndPoint endpoint.Endpoint
   LoginEndPoint    endpoint.Endpoint
}

func NewEndpoints(svc service.Service) EndPoints {
   var multiplyEndpoint endpoint.Endpoint
   {
      multiplyEndpoint = makeMultiplyMultiplyEndPoint(svc)
      multiplyEndpoint = AuthMiddleware()(multiplyEndpoint)
   }

   var loginEndpoint endpoint.Endpoint
   {
      loginEndpoint = makeLoginEndPoint(svc)
   }

   return EndPoints{MultiplyEndPoint: multiplyEndpoint, LoginEndPoint: loginEndpoint}
}

func makeMultiplyMultiplyEndPoint(s service.Service) endpoint.Endpoint {
   return func(ctx context.Context, request interface{}) (response interface{}, err error) {
      reqId := ctx.Value(utils.BaseRequestId).(string)
      log.GetLogger().Info("endpoint layer: reqId=" + reqId)
      req := request.(service.MultiplyRequest)
      resp := s.Multiply(ctx, req)
      return resp, nil
   }
}

func makeLoginEndPoint(s service.Service) endpoint.Endpoint {
   return func(ctx context.Context, request interface{}) (response interface{}, err error) {
      reqId := ctx.Value(utils.BaseRequestId).(string)
      log.GetLogger().Info("endpoint layer: reqId=" + reqId)
      req := request.(service.LoginRequest)
      resp, err := s.Login(ctx, req)
      return resp, err
   }
}

token验证中间件auth_milddleware.go:

代码语言:txt
复制
package endpoint

import (
   "context"
   "errors"
   "github.com/go-kit/kit/endpoint"
   "go-kit-microservice/internal/pkg/utils"
)

func AuthMiddleware() endpoint.Middleware {
   return func(next endpoint.Endpoint) endpoint.Endpoint {
      return func(ctx context.Context, request interface{}) (response interface{}, err error) {
         token := ctx.Value(utils.JwtTokenKey).(string)
         if token == "" {
            return "nil", errors.New("请先登录")
         }

         jwtInfo, err := utils.ParseToken(token)
         if err != nil {
            return "", err
         }

         if v, ok := jwtInfo["Account"]; ok {
            ctx = context.WithValue(ctx, "account", v)
         }
         return next(ctx, request)
      }
   }
}

transport.go

代码语言:txt
复制
package transport

import (
   "context"
   "encoding/json"
   "fmt"
   "github.com/go-kit/kit/endpoint"
   httptransport "github.com/go-kit/kit/transport/http"
   uuid "github.com/satori/go.uuid"
   endpoints "go-kit-microservice/internal/endpoint"
   log "go-kit-microservice/internal/pkg/log"
   "go-kit-microservice/internal/pkg/utils"
   "go-kit-microservice/internal/service"
   "net/http"
   "strconv"
)

func MewHttpHandler(endpoints endpoints.EndPoints) http.Handler {
   options := []httptransport.ServerOption{

      // Unified exception handling
      httptransport.ServerErrorEncoder(errorEncoder),

      // before request set request id
      httptransport.ServerBefore(func(ctx context.Context, request *http.Request) context.Context {
         reqId := uuid.NewV5(uuid.NewV4(), "req_id").String()
         ctx = context.WithValue(ctx, utils.BaseRequestId, reqId)
         ctx = context.WithValue(ctx, utils.JwtTokenKey, request.Header.Get("Authorization"))
         return ctx
      }),
   }

   m := http.NewServeMux()
   m.Handle("/multiply", httptransport.NewServer(
      endpoints.MultiplyEndPoint,
      decodeMultiplyRequest,
      encodeGeneralResponse,
      options...,
   ))

   m.Handle("/login", httptransport.NewServer(
      endpoints.LoginEndPoint,
      decodeLoginRequest,
      encodeGeneralResponse,
      options...,
   ))

   return m
}

// decode the request to service layer request params
func decodeMultiplyRequest(ctx context.Context, r *http.Request) (interface{}, error) {

   reqId := ctx.Value(utils.BaseRequestId).(string)
   msg := fmt.Sprintf("%s\t%s\t%s\treqId=%s\t%s\t%s\t",
      utils.RemoteIp(r),
      r.Method,
      r.RequestURI,
      reqId,
      r.Proto,
      r.Header.Get("User-Agent"))

   log.GetLogger().Info("decode request:" + msg)

   var (
      in  service.MultiplyRequest
      err error
   )

   if in.A, err = strconv.Atoi(r.FormValue("a")); err != nil {
      log.GetLogger().Error("decode request param a failed:" + err.Error())
      return in, err
   }

   if in.B, err = strconv.Atoi(r.FormValue("b")); err != nil {
      log.GetLogger().Error("decode request param b failed:" + err.Error())
      return in, err
   }

   return in, nil
}

// encode the response data to user
func encodeGeneralResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
   if f, ok := response.(endpoint.Failer); ok && f.Failed() != nil {
      errorEncoder(ctx, f.Failed(), w)
      return nil
   }

   w.Header().Set("Content-Type", "application/json; charset=utf-8")
   return json.NewEncoder(w).Encode(response)
}

// decode the request to service layer request params
func decodeLoginRequest(ctx context.Context, r *http.Request) (interface{}, error) {
   var req service.LoginRequest
   err := json.NewDecoder(r.Body).Decode(&req)
   if err != nil {
      return nil, err
   }
   return req, nil
}

// error EncodeHandler
func errorEncoder(ctx context.Context, err error, w http.ResponseWriter) {
   w.WriteHeader(http.StatusOK)
   log.GetLogger().Error(err.Error())
   e := json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()})
   if e != nil {
      log.GetLogger().Error("json encode failed: " + e.Error())
   }
}

type errorWrapper struct {
   Error string `json:"error"`
}

流程说明:

  1. 用户使用/login登录,输入AccuntId和PassWord后如果验证成功,就会生成一个token并返回给客户端
  2. 用户每次请求的时候,除非是/login接口,其他服务都需要在header里面携带Authorization参数,里面填的是login之后返回的token值。
  3. token如果校验通过,才会走正常的业务流程,否则返回验证失败。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是JWT?
  • 使用
    • 流程说明:
    相关产品与服务
    消息队列 TDMQ
    消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档