'Gin(十二):配合JWT,go語言最火框架之一'

Go語言 算法 JSON 代碼獵奇站 2019-08-09
"
"
Gin(十二):配合JWT,go語言最火框架之一

在前後端分離的項目中,越來越多的項目採用 JWT 代替傳統的 cookie ,這裡我們來使用 JWT 結合 Gin 來作為一個登錄授權和權限校驗。

什麼是 JWT

JWT 的全稱叫做 JSON WEB TOKEN,在目前前後端系統中使用較多。

JWT 構成

JWT 是由三段構成的。分別是 HEADER,PAYLOAD,VERIFY SIGNATURE,它們生成的信息通過 . 分割。

"
Gin(十二):配合JWT,go語言最火框架之一

在前後端分離的項目中,越來越多的項目採用 JWT 代替傳統的 cookie ,這裡我們來使用 JWT 結合 Gin 來作為一個登錄授權和權限校驗。

什麼是 JWT

JWT 的全稱叫做 JSON WEB TOKEN,在目前前後端系統中使用較多。

JWT 構成

JWT 是由三段構成的。分別是 HEADER,PAYLOAD,VERIFY SIGNATURE,它們生成的信息通過 . 分割。

Gin(十二):配合JWT,go語言最火框架之一

HEADER

header 是由 一個 typ 和 alg 組成,typ 會指明為 JWT,而 alg 是所使用的加密算法。

 {
"alg": "HS256",
"typ": "JWT"
}

PAYLOAD

payload 是 JWT 的載體,也就是我們要承載的信息。這段信息是我們可以自定義的,可以定義我們要存放什麼信息,那些字段。該部分信息不宜過多,它會影響 JWT 生成的大小,還有就是請勿將敏感數據存入該部分,該端數據前端是可以解析獲取 token 內信息的。

官方給了七個默認字段,我們可以不全部使用,也可以加入我們需要的字段。

  • Audience表示JWT的受眾
  • ExpiresAt失效時間
  • Id簽發編號
  • IssuedAt簽發時間
  • Issuer簽發人
  • NotBefore生效時間
  • Subject主題

VERIFY SIGNATURE

這也是 JWT 的最後一段,該部分是由算法計算完成的。

對剛剛的 header 進行 base64Url 編碼,對 payload 進行 base64Url 編碼,兩端完成編碼後通過 . 進行連接起來。

 base64UrlEncode(header).base64UrlEncode(payload)

完成上述步驟後,就要通過我們 header 裡指定的加密算法對上部分進行加密,同時我們還要插入我們的一個密鑰,來確保我的 JWT 簽發是安全的。

這便是我們的第三部分。

當三部分都完成後,通過使用 . 將三部分分割,生成了上圖所示的 JWT 。

JWT 登錄原理

簡單的說就是當用戶登錄的時候,服務器校驗登錄名稱和密碼是否正確,正確的話,會生成 JWT 返回給客戶端。客戶端獲取到 JWT 後要進行保存,之後的每次請求都會講 JWT 攜帶在頭部,每次服務器都會獲取頭部的 JWT 是否正確,如果正確則正確執行該請求,否者驗證失敗,重新登錄。

"
Gin(十二):配合JWT,go語言最火框架之一

在前後端分離的項目中,越來越多的項目採用 JWT 代替傳統的 cookie ,這裡我們來使用 JWT 結合 Gin 來作為一個登錄授權和權限校驗。

什麼是 JWT

JWT 的全稱叫做 JSON WEB TOKEN,在目前前後端系統中使用較多。

JWT 構成

JWT 是由三段構成的。分別是 HEADER,PAYLOAD,VERIFY SIGNATURE,它們生成的信息通過 . 分割。

Gin(十二):配合JWT,go語言最火框架之一

HEADER

header 是由 一個 typ 和 alg 組成,typ 會指明為 JWT,而 alg 是所使用的加密算法。

 {
"alg": "HS256",
"typ": "JWT"
}

PAYLOAD

payload 是 JWT 的載體,也就是我們要承載的信息。這段信息是我們可以自定義的,可以定義我們要存放什麼信息,那些字段。該部分信息不宜過多,它會影響 JWT 生成的大小,還有就是請勿將敏感數據存入該部分,該端數據前端是可以解析獲取 token 內信息的。

官方給了七個默認字段,我們可以不全部使用,也可以加入我們需要的字段。

  • Audience表示JWT的受眾
  • ExpiresAt失效時間
  • Id簽發編號
  • IssuedAt簽發時間
  • Issuer簽發人
  • NotBefore生效時間
  • Subject主題

VERIFY SIGNATURE

這也是 JWT 的最後一段,該部分是由算法計算完成的。

對剛剛的 header 進行 base64Url 編碼,對 payload 進行 base64Url 編碼,兩端完成編碼後通過 . 進行連接起來。

 base64UrlEncode(header).base64UrlEncode(payload)

完成上述步驟後,就要通過我們 header 裡指定的加密算法對上部分進行加密,同時我們還要插入我們的一個密鑰,來確保我的 JWT 簽發是安全的。

這便是我們的第三部分。

當三部分都完成後,通過使用 . 將三部分分割,生成了上圖所示的 JWT 。

JWT 登錄原理

簡單的說就是當用戶登錄的時候,服務器校驗登錄名稱和密碼是否正確,正確的話,會生成 JWT 返回給客戶端。客戶端獲取到 JWT 後要進行保存,之後的每次請求都會講 JWT 攜帶在頭部,每次服務器都會獲取頭部的 JWT 是否正確,如果正確則正確執行該請求,否者驗證失敗,重新登錄。

Gin(十二):配合JWT,go語言最火框架之一

Gin 生成 JWT

go 語言的 JWT 庫有很多。jwt.io 上也給出了很多 。這裡使用 jwt-go

"github.com/dgrijalva/jwt-go"

我們對登錄方法進行改造。

 // 省略代碼
expiresTime := time.Now().Unix() + int64(config.OneDayOfHours)
claims := jwt.StandardClaims{
Audience: user.Username, // 受眾
ExpiresAt: expiresTime, // 失效時間
Id: string(user.ID), // 編號
IssuedAt: time.Now().Unix(), // 簽發時間
Issuer: "gin hello", // 簽發人
NotBefore: time.Now().Unix(), // 生效時間
Subject: "login", // 主題
}
var jwtSecret = []byte(config.Secret)
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 省略代碼

這裡的 config.OneDayOfHours 設定了過期時間,這裡設定了一天。通過 StandardClaims 生成標準的載體,也就是上文提到的七個字段,其中 編號設定為 用戶 id。其中的 jwtSecret 是我們設定的密鑰,

我們這裡通過 HS256 算法生成 tokenClaims ,這就是我們的 HEADER 部分和 PAYLOAD。

 token, err := tokenClaims.SignedString(jwtSecret)

這樣便生成了我們的 token 。我們要將我們的 token 和 Bearer 拼接在一起,同時中間用空格隔開。

 token = "Bearer "+ token

生成 Bearer Token 。

當我們用戶進行登錄的時候,就可以通過該片段生成 JWT。

下面是完整代碼:

 func CreateJwt(ctx *gin.Context) {
// 獲取用戶
user := &model.User{}
result := &model.Result{
Code: 200,
Message: "登錄成功",
Data: nil,
}
if e := ctx.BindJSON(&user); e != nil {
result.Message = "數據綁定失敗"
result.Code = http.StatusUnauthorized
ctx.JSON(http.StatusUnauthorized, gin.H{
"result": result,
})
}
u := user.QueryByUsername()
if u.Password == user.Password {
expiresTime := time.Now().Unix() + int64(config.OneDayOfHours)
claims := jwt.StandardClaims{
Audience: user.Username, // 受眾
ExpiresAt: expiresTime, // 失效時間
Id: string(user.ID), // 編號
IssuedAt: time.Now().Unix(), // 簽發時間
Issuer: "gin hello", // 簽發人
NotBefore: time.Now().Unix(), // 生效時間
Subject: "login", // 主題
}
var jwtSecret = []byte(config.Secret)
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
if token, err := tokenClaims.SignedString(jwtSecret); err == nil {
result.Message = "登錄成功"
result.Data = "Bearer " + token
result.Code = http.StatusOK
ctx.JSON(result.Code, gin.H{
"result": result,
})
} else {
result.Message = "登錄失敗"
result.Code = http.StatusOK
ctx.JSON(result.Code, gin.H{
"result": result,
})
}
} else {
result.Message = "登錄失敗"
result.Code = http.StatusOK
ctx.JSON(result.Code, gin.H{
"result": result,
})
}
}

通過 .http 請求測試,結果如下

 {
"result": {
"code": 200,
"message": "登錄成功",
"data": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxMjMiLCJleHAiOjE1NjQ3OTY0MzksImp0aSI6Ilx1MDAwMCIsImlhdCI6MTU2NDc5NjQxOSwiaXNzIjoiZ2luIGhlbGxvIiwibmJmIjoxNTY0Nzk2NDE5LCJzdWIiOiJsb2dpbiJ9.CpacmfBSMgmK2TgrT-KwNB60bsvwgyryGQ0pWZr8laU"
}
}

這個便完成了token的生成。

Gin 校驗 Token

那麼,接下來就需要完成 token 的驗證。

還記得之前我們驗證用戶是否授權採用的辦法嗎?是的,在中間件裡查看用戶 cookie。同樣的方法,我們這裡校驗用戶 JWT 是否有效。

編寫我們的中間件。

新建立 middleware/Auth.go

首先先編寫我們的解析 token 方法,parseToken()

 func parseToken(token string) (*jwt.StandardClaims, error) {
jwtToken, err := jwt.ParseWithClaims(token, &jwt.StandardClaims{}, func(token *jwt.Token) (i interface{}, e error) {
return []byte(config.Secret), nil
})
if err == nil && jwtToken != nil {
if claim, ok := jwtToken.Claims.(*jwt.StandardClaims); ok && jwtToken.Valid {
return claim, nil
}
}
return nil, err
}

通過傳入我們的 token , 來對 token 進行解析。

完整的中間件代碼

 func Auth() gin.HandlerFunc {
return func(context *gin.Context) {
result := model.Result{
Code: http.StatusUnauthorized,
Message: "無法認證,重新登錄",
Data: nil,
}
auth := context.Request.Header.Get("Authorization")
if len(auth) == 0 {
context.Abort()
context.JSON(http.StatusUnauthorized, gin.H{
"result": result,
})
}
auth = strings.Fields(auth)[1]
// 校驗token
_, err := parseToken(auth)
if err != nil {
context.Abort()
result.Message = "token 過期" + err.Error()
context.JSON(http.StatusUnauthorized, gin.H{
"result": result,
})
} else {
println("token 正確")
}
context.Next()
}
}

首先在請求頭獲取 token ,然後對先把 token 進行解析,將 Bearer 和 JWT 拆分出來,將 JWT 進行校驗。

我們只需要對我們需要校驗的路由進行添加中間件校驗即可。

 router.GET("/", middleware.Auth(), func(context *gin.Context) {
context.JSON(http.StatusOK, time.Now().Unix())
})

當我們訪問 / 的時候就需要攜帶 token 了

 GET http://localhost:8080
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxMjMiLCJleHAiOjE1NjQ3OTQzNjIsImp0aSI6Ilx1MDAwMCIsImlhdCI6MTU2NDc5NDM0MiwiaXNzIjoiZ2luIGhlbGxvIiwibmJmIjoxNTY0Nzk0MzQyLCJzdWIiOiJsb2dpbiJ9.uQxGMsftyVFtYIGwQVm1QB2djw-uMfDbw81E5LMjliU

✍總結

本章節對什麼是 JWT,Gin 中如何使用 JWT 做了介紹。但是不要過於迷信 JWT,JWT 還有很多問題,比如說 JWT 失效只能是時間過期,如果修改密碼或者賬戶註銷等操作需要我們另外添加邏輯判斷。適合的地方選用適合的技術才能發揮最大的優勢。

‍本章節代碼

Github 點擊瞭解更多查看

推薦閱讀

Gin(一):Hello Gin ,學習 Gin 從這裡開始

Gin(二):路由Router ,go語言框架學習

Gin(三):與模板配合使用 tmpl,go web 開發最火框架之一

Gin(四):接收表單數據和模型綁定,Go語言最火web框架之一

Gin(五):連接MySQL , Go 輕量級框架 Gin

Gin(六):文件的上傳,go語言最火框架之一

Gin(七):中間件的介紹和使用,GO語言最火的框架之一

Gin(八):cookies使用,Go語言web最火框架之一

Gin(九):生成restful 接口,go語言最火web框架之一

Gin(十): 集成 Swagger,Gin配置這個神器,再也不用寫接口文檔了

Gin(十): 集成 Swagger,Gin配置這個神器,再也不用寫接口文檔了

"

相關推薦

推薦中...