JWT 在 Gin 中的使用

原文:JWT 在 Gin 中的使用git

介紹

JSON Web Token (JWT), 是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準((RFC 7519).該 Token 被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT 的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該 Token 也可直接被用於認證,也可被加密。github

使用

安裝

go get github.com/appleboy/gin-jwt

引入算法

import "github.com/appleboy/gin-jwt"

我目前使用的版本是 v2.5.0.api

<!-- more -->安全

建立中間件

設計 API 對象bash

type API struct {
    App    *apps.App  // 業務對象
    Router *gin.Engine // 路由
    JWT    *jwt.GinJWTMiddleware // jwt 對象
}

中間件對象:服務器

api.JWT = &jwt.GinJWTMiddleware{
        Realm:      "gin jwt",
        Key:        []byte("secret key"),
        Timeout:    time.Hour,
        MaxRefresh: time.Hour,
        PayloadFunc: func(data interface{}) jwt.MapClaims {},
        Authenticator: func(c *gin.Context) (interface{}, error) {},
        Authorizator: func(data interface{}, c *gin.Context) bool {},
        Unauthorized: func(c *gin.Context, code int, message string) {},
        TokenLookup: "header: Authorization, query: token, cookie: jwt",
        // TokenLookup: "query:token",
        // TokenLookup: "cookie:token",
        TokenHeadName: "Bearer",
        TimeFunc: time.Now,
    }
  • Realm JWT標識
  • Key 服務端密鑰
  • Timeout token 過時時間
  • MaxRefresh token 更新時間
  • PayloadFunc 添加額外業務相關的信息
  • Authenticator 在登陸接口中使用的驗證方法,並返回驗證成功後的用戶對象。
  • Authorizator 登陸後其餘接口驗證傳入的 token 方法
  • Unauthorized 驗證失敗後設置錯誤信息
  • TokenLookup 設置 token 獲取位置,通常默認在頭部的 Authorization 中,或者 query的 token 字段,cookie 中的 jwt 字段。
  • TokenHeadName Header中 token 的頭部字段,默認經常使用名稱 Bearer
  • TimeFunc 設置時間函數

註冊階段

在註冊時若是要直接返回 token,那麼能夠調用 TokenGenerator 來生成 token。cookie

token, expire, err := c.JWT.TokenGenerator(strconv.Itoa(user.ID), *user)

TokenGenerator 的具體實現網絡

func (mw *GinJWTMiddleware) TokenGenerator(userID string, data interface{}) (string, time.Time, error) {
   // 根據簽名算法建立 token 對象
    token := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm))
    // 獲取 claims
    claims := token.Claims.(jwt.MapClaims)

   // 設置業務中須要的額外信息
    if mw.PayloadFunc != nil {
        for key, value := range mw.PayloadFunc(data) {
            claims[key] = value
        }
    }

   // 過時時間
    expire := mw.TimeFunc().UTC().Add(mw.Timeout)
    claims["id"] = userID
    claims["exp"] = expire.Unix()
    claims["orig_iat"] = mw.TimeFunc().Unix()
    // 生成 token 
    tokenString, err := mw.signedString(token) 
    if err != nil {
        return "", time.Time{}, err
    }

    return tokenString, expire, nil
}

登陸階段

登陸時會調用 Authenticator 註冊的方法。app

func (api *API) LoginAuthenticator(ctx *gin.Context) (interface{}, error) {
    var params model.UserParams
    if err := ctx.Bind(&params); err != nil {
        return "", jwt.ErrMissingLoginValues
    }

   // 根據用戶名獲取用戶
    user, err := api.App.GetUserByName(params.Username)
    if err != nil {
        return nil, err
    }

   // 驗證密碼
    if user.AuthPassword(params.Password) {
        return *user, nil
    }

    return nil, jwt.ErrFailedAuthentication
}

驗證 Token

其餘接口在設置了中間件 Router.Use(api.JWT.MiddlewareFunc()) 後,經過調用 Authorizator 方法來驗證。

func (api *API) LoginedAuthorizator(data interface{}, c *gin.Context) bool {
    if id, ok := data.(string); ok {
        return api.App.IsExistUser(id)
    }
    return false
}

在業務 Hander 中能夠經過方法 jwt.ExtractClaims(ctx) 來獲取 payload 的信息。

深刻

gin-jwt 依賴的 jwt 庫叫作 jwt-go。下面來介紹一下這個庫。

核心的 Token 結構:

// A JWT Token.  Different fields will be used depending on whether you're
// creating or parsing/verifying a token.
type Token struct {
    Raw       string                 // The raw token.  Populated when you Parse a token
    Method    SigningMethod          // The signing method used or to be used
    Header    map[string]interface{} // The first segment of the token
    Claims    Claims                 // The second segment of the token
    Signature string                 // The third segment of the token.  Populated when you Parse a token
    Valid     bool                   // Is the token valid?  Populated when you Parse/Verify a token
}

這個Token結構體是用來生成 jwt 的 token。其中 Method 是用來表示簽名使用的算法。Header 是頭部jwt的信息,還有 Claims 記錄額外的信息。

而後是生成簽名的方法,key 是服務端的密鑰。

func (t *Token) SignedString(key interface{}) (string, error) {
    var sig, sstr string
    var err error
    // 將 Header 和 Claims 轉換成字符串而後 base64 以後拼接在一塊兒。
    if sstr, err = t.SigningString(); err != nil {
        return "", err
    }
    // 使用簽名算法加密
    if sig, err = t.Method.Sign(sstr, key); err != nil {
        return "", err
    }
    return strings.Join([]string{sstr, sig}, "."), nil
}

解密 token 的對象叫作 Parser

type Parser struct {}

// 主要解析方法
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {}

Parser 除了驗證 Token 外,還包括解碼 Header 和 Claims 的內容。

資源

相關文章
相關標籤/搜索