原文: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(¶ms); 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 }
其餘接口在設置了中間件 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 的內容。