go micro jwt 網關鑑權

本文介紹go micro中的api訪問權限控制,這裏僅僅介紹內部服務間的互相調用鑑權(jwt),不是針對用戶系統的細緻rbac模式。git

目標:github

  1. 內部服務間調用應在header上帶上jwt token,不然返回錯誤提示信息
  2. 有2種方式web

    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. 業務調用步驟json

    1. 調用GetJwt()獲取jwt token
    2. 調用其餘接口時,在header加入jwt token

首先建立一個api項目segmentfault

micro new --type=api myauthapi

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網關代碼app

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/...

go micro 分析系列文章
go micro server 啓動分析
go micro client
go micro broker
go micro cmd
go micro config
go micro store
go micro registry
go micro router
go micro runtime
go micro transport
go micro web
go micro registry 插件consul
go micro plugin
go micro jwt 網關鑑權
go micro 鏈路追蹤
go micro 熔斷與限流
go micro wrapper 中間件
go micro metrics 接入Prometheus、Grafana

相關文章
相關標籤/搜索