乐趣区

关于micro:go-micro-jwt-网关鉴权

本文介绍 go micro 中的 api 拜访权限管制,这里仅仅介绍外部服务间的相互调用鉴权 (jwt),不是针对用户零碎的粗疏 rbac 模式。

指标:

  1. 外部服务间调用应在 header 上带上 jwt token,否则返回谬误提示信息
  2. 有 2 种形式

    1. 在每个服务上各自鉴权
    2. 在网关上鉴权,各服务只需注册一个 wrapper 查看 header 中 jwt 信息鉴权。这样做有个前提,所有服务需放到同一个网络内,走对立的服务注册核心,这里次要介绍这种形式。
  3. 先创立一个 myauth 的服务,对外提供 2 个接口

    1. GetJwt() 返回 jwt token,能够用用户名明码或其余形式验证,依据具体业务灵便决定即可,这里示例采纳服务名和密钥方
    2. InspectJwt() 验证 jwt token 是否无效
  4. 自定义一个网关,通过插件形式 wrapper 指定一个鉴权函数,除了第 3 步中的 2 个接口外,其余都须要 header 中有 jwt 的 token
  5. 其余业务只须要注册一个 wrapper 查看 header 中 jwt 信息鉴权
  6. 业务调用步骤

    1. 调用 GetJwt() 获取 jwt token
    2. 调用其余接口时,在 header 退出 jwt token

首先创立一个 api 我的项目

micro new --type=api myauth

GetJwt()、InspectJwt() 相干代码

// Myauth.Call is called by the API as /myauth/call with post body {"name": "foo"}
func (e *Myauth) GetJwt(ctx context.Context, req *api.Request, rsp *api.Response) error {log.Info("Received Myauth.GetJwt request")

    getmap := req.GetGet()
    log.Info("Received getmap %+v\n", getmap)
    postmap := req.GetPost()
    log.Info("Received postmap %+v\n", postmap)

    // 外部服务调用底层 service,通过 jwt 验证
    // 定义服务名和 key,通过这 2 个参数获取 jwt access token
    // serviceName := "order"
    // serviceKey := "123456"
    // 比照 serviceName\serviceKey 也能够是用户名明码等,这里的示例为了不便硬编码在代码中
    // 理论我的项目中应该从数据库或文件读取
    serviceName := extractValue(postmap["service"])
    serviceKey := extractValue(postmap["key"])
    log.Info("serviceName %+v\n", serviceName, serviceKey)
    if serviceName != "order" || serviceKey != "123456" {Rsp(rsp, 403, "服务名称或 key 谬误", nil)
        return nil
    }

    // 生成 jwt
    // expireTime := time.Now().Add(time.Hour * 24 * 3).Unix()
    expireTime := time.Now().Add(time.Second * 60 * 60).Unix()
    token := &token.Token{}
    token.Init([]byte("key123456")) // 理论我的项目需从数据库或文件读取
    jwtstring, err := token.Encode("auth jwt", serviceName, expireTime)
    if err != nil {Rsp(rsp, 403, "jwt 生成谬误", nil)
        return nil
    }

    msg := make(map[string]interface{})
    msg["jwt"] = jwtstring
    Rsp(rsp, 200, "ok", msg)
    return nil
}

// 验证 jwt
func (e *Myauth) InspectJwt(ctx context.Context, req *api.Request, rsp *api.Response) error {log.Info("Received Myauth.InspectJwt request")
    // getmap := req.GetGet()
    // log.Info("Received getmap %+v\n", getmap)
    postmap := req.GetPost()
    // log.Info("Received postmap %+v\n", postmap)

    jwtString := extractValue(postmap["jwt"])
    log.Info("jwtString %+v\n", jwtString)
    if len(jwtString) == 0 {Rsp(rsp, 403, "jwt 参数谬误", nil)
        return nil
    }

    // 解析 jwt
    token := &token.Token{}
    token.Init([]byte("key123456"))
    info, err := token.Decode(jwtString)
    if err != nil {Rsp(rsp, 403, "jwt 解析谬误", nil) // 过期或 jwt 有问题
        return nil
    }

    t := make(map[string]interface{})
    t["data"] = info
    Rsp(rsp, 200, "ok", t)
    return nil
}

// 返回 func
func Rsp(rsp *api.Response, code int, err string, msg map[string]interface{}) error {
    if msg == nil {msg = make(map[string]interface{})
    }
    r := &rspMsg{
        Code: code,
        Err:  err,
        Msg:  msg,
    }

    b, err2 := json.Marshal(r)
    if err2 != nil {log.Info("json.Marshal err", err2)
    }
    rsp.StatusCode = int32(code)
    rsp.Body = string(b)
    return nil
}

自定义 micro 网关代码

package main

import (
    "net/http"

    "myauth/lib/token"

    log "github.com/micro/go-micro/v2/logger"
    "github.com/micro/micro/v2/cmd"
    "github.com/micro/micro/v2/plugin"
)

func main() {tk := &token.Token{}
    tk.Init([]byte("key123456"))
    plugin.Register(plugin.NewPlugin(plugin.WithName("auth"),
        plugin.WithHandler(JWTAuthWrapper(tk),
        ),
    ))

    cmd.Init()}

func JWTAuthWrapper(t *token.Token) plugin.Handler {return func(h http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {log.Info("===========", r.URL.Path)
            // 不须要登录的 url 地址 strings.HasPrefix(r.URL.Path, "/hello") ||
            if r.URL.Path == "/myauth/Myauth/GetJwt" ||
                r.URL.Path == "/myauth/Myauth/InspectJwt" {h.ServeHTTP(w, r)
                return
            }

            // tokenstr := r.Header.Get("Authorization")// 当初不能够用 Authorization,须要用 Bearer
            tokenstr := r.Header.Get("Bearer")
            log.Info("tokenstr", tokenstr)
            userFromToken, e := t.Decode(tokenstr)
            log.Info("userFromToken", userFromToken)
            if e != nil {_, _ = w.Write([]byte("unauthorized"))
                return
            }

            // r.Header.Set("X-Example-Username", userFromToken.UserName)
            h.ServeHTTP(w, r)
            return
        })
    }
}

示例代码见 https://github.com/wulinlw/mi…

参考

go-micro 网关鉴权之 jwt

https://www.jianshu.com/p/426…

【go 语言微服务实际】#5-go-micro 实现 JWT 认证

https://juejin.im/post/5e6123…

https://github.com/Allenxuxu/…

退出移动版