Gin(十五):JWT使用(續)

youngxhui發表於2019-10-28

Gin 教程叕來了(說好的大結局呢?),這次主要是來說明 JWT 存在的一些問題和解決方案。如果你還不知道 JWT 是什麼,建議瞭解一下,也可以翻翻前面的文章看看。

?問題所在

上次寫 JWT 的時候,和群裡的小夥伴討論,有小夥伴提出了一些問題,使用者修改密碼或者登出賬戶怎麼辦?

是啊,怎麼辦?

我們都知道 JWT 是否有效,靠的是失效時間,而且伺服器不對使用者 JWT 進行儲存,也就是一旦簽發了的 JWT ,伺服器失去了對其的控制權,只能等其過期而失效。如果這樣,那麼使用者修改密碼或者登出之後,之前的 JWT 仍舊還可以使用,那麼就會帶來安全風險。

於是乎,我默默的開啟了百(gu)度(ge)

??????

你們為什麼要把 JWT 存在 redis 裡?

這是什麼操作?一旦伺服器去儲存 JWT,還用什麼 JWT ?普通 token 就完全可以了啊,何必多此一舉。

以上就是我們在使用 JWT 的時候產生的問題。

?解決問題

既然有了問題,就要解決。

再次回顧一下 JWT 的使用流程。

Gin(十五):JWT使用(續)

  1. 瀏覽器發出登入請求,將賬號和密碼提交到伺服器;

  2. 伺服器建立 JWT 並且加密(並不是全部加密,僅僅加密 VERIFY SIGNATURE);

  3. JWT 返回瀏覽器;

  4. 訪問需要授權的介面,將 JWT 攜帶在Header;

  5. 校驗 JWT ,並且從 JWT 獲取 JWT

  6. 響應瀏覽器。

上面的六個步驟就是 JWT 使用的過程。伺服器只做了兩步,生成 JWT 和校驗 JWT 。而我們解決上述提出的問題,就在伺服器端解決。

我們都知道在加密的時候伺服器端會儲存一個金鑰,而校驗解密的時候就需要這個金鑰,這個金鑰就是我們解決該問題的 KEY

如果我們把金鑰設成動態的,使用者密碼修改或者登出的時候,該金鑰就會發生改變,那麼之前簽發的 JWT 無法使用新的金鑰來解密了,也就意味著之前的金鑰已經失效了。而這個動態金鑰應該保證獨立,每個使用者的金鑰應該是不一樣的,該使用者的金鑰只負責該使用者的 JWT

於是乎,問題似乎已經解決了,我們可以在資料庫使用者表中設定一個單獨的欄位來標識,每次發生密碼修改或者賬號登出的時候,只需要修改該欄位就可以保證之前的JWT失效。或者使用使用者的密碼作為金鑰。

部分程式碼

UserHandler.go

// 通過密碼和保留欄位加密
var jwtSecret = []byte(config.Secret + u.Password)
token, err := tokenClaims.SignedString(jwtSecret)
複製程式碼

其中 config.Secret 是我們伺服器上的固定金鑰。

Auth.go

// 分割出來載體
payload := strings.Split(token, ".")
bytes, e := jwt.DecodeSegment(payload[1])

if e != nil {
	println(e.Error())
}
content := ""
for i := 0; i < len(bytes); i++ {
	content += string(bytes[i])
}
split := strings.Split(content, ",")
id := strings.SplitAfter(split[2], ":")
i := strings.Split(id[1], "\\u")
i = strings.Split(i[1], "\"")

ID, err := strconv.Atoi(i[0])
if err != nil {
	println(err.Error())
}

user := model.User{}
user.ID = uint(ID)
u := model.User.QueryById(user)
jwtToken, err := jwt.ParseWithClaims(token, &jwt.StandardClaims{},
	func(token *jwt.Token) (i interface{}, e error) {
		return []byte(config.Secret + u.Password), nil
	})
複製程式碼

這裡就不展示全部程式碼了。大家可以看 Github gin_jwt_2 分支上檢視。

個人公眾號

歡迎各位大佬關注。

Gin(十五):JWT使用(續)

相關文章