淺談json web token及應用

Json Web Token (JWT),是一個很是輕巧的規範,這個規範容許在網絡應用環境間客戶端和服務器間較安全的傳遞信息。該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源。html

image

在web應用中,咱們提供的API接口,經過GET或者POST方式調用,在調用過程當中,就存在着接口認證及數據的安全性問題。例如以下問題:前端

一、請求來自哪裏,身份是否合法?java

二、請求參數是否被篡改?python

三、客戶端請求的惟一性,是否爲重複請求攻擊(RepeatAttack)?git

傳統的Session認證方式

在傳統的web應用中,服務端成功相應請求者,返回正常的response依賴於服務端經過一種存儲機制把每一個用戶通過認證以後的會話信息(session)記錄服務器端,通常記錄到內存、磁盤、數據庫中,這種方式在請求量和用戶量增多的時候無疑會增大服務端的開銷;若是是記錄在內存中,那每次請求都分發登到該機器上才能受權獲取資源,那在分佈式系統中就存在着問題;由於是基於Cookie的,若是Cookie被截獲,攻擊者會盜用身份信息進行發送惡意請求,也就是「跨站請求僞造」(CSRF)。github

基於token的認證方式

客戶端用用戶名和密碼通過服務器認證以後,服務器會簽發一個token返回給客戶端,客戶端存儲token(通常存在請求頭裏),而且在以後的請求裏附帶此token,服務器每次會解簽名token,驗證經過則返回資源。另外服務端要支持CORS跨來源資源共享)策略,服務器處理完請求以後,會再返回結果中加上Access-Control-Allow-Origin。web

jwt的生成

token是接口的令牌,比如去衙門辦事,「衙門口朝南開,有理無錢莫進來」。沒有令牌就別想辦事。token的驗證方法不少,也生成了不少標準,jwt就是一種基於json的RFC 7519。該標準由三部分組成:面試

  • header算法

  • payloadspring

  • signature

header和payload通過base64編碼後用點拼接起來。signature是把header和payload編碼和拼接後通過加密算法加密,加密時還要一個密碼,這個密碼保存在服務器端。大體示意圖以下:

image

Header:

head由兩部分組成,一個是token類型,一個是使用的算法,以下類型爲jwt,使用的算法是HS256。固然,還有HS38四、HS512算法。

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

將以上json進行base64編碼,固然編碼前將json去格式化,如圖:

image

生成的編碼爲:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

用go語言實現:

package main

import (
 "fmt"
 "encoding/base64"
)

func main() {

 head1 := `{"typ":"JWT","alg":"HS256"}`

 fmt.Println(base64.StdEncoding.EncodeToString([]byte(head1)))
}

Payload:

payload 裏面是 token 的具體內容,這些內容裏面有一些是標準字段,咱們也能夠添加自定義內容。以下:

{
   "iss": "smallsoup",
   "iat": 1528902195,
   "exp": 1528988638,
   "aud": "www.smallsoup.com",
   "sub": "smallsoup@qq.com",
   "userId": "0418"
}

這裏面的前五個字段都是由JWT的標準所定義的,在jwt標準中均可以找到。

  • iss: 該JWT的簽發者

  • sub: 該JWT所面向的用戶

  • aud: 接收該JWT的一方

  • exp(expires): 何時過時,這裏是一個Unix時間戳

  • iat(issued at): 在何時簽發的

最後一個userId表示了用戶信息,爲自定義字段,咱們也能夠定義角色等其餘字段。以上的json去格式化後的base64編碼以下:

eyJpc3MiOiJzbWFsbHNvdXAiLCJpYXQiOjE1Mjg5MDIxOTUsImV4cCI6MTUyODk4ODYzOCwiYXVkIjoid3d3LnNtYWxsc291cC5jb20iLCJzdWIiOiJzbWFsbHNvdXBAcXEuY29tIiwidXNlcklkIjoiMDQxOCJ9

image

Signature:

JWT 的最後一部分是 Signature ,這部份內容有三個部分,先是用 Base64 編碼的 header.payload ,再用加密算法加密一下,加密的時候要放進去一個 Secret ,這個至關因而一個密碼,這個密碼祕密地存儲在服務端。

  • header

  • payload

  • secret

假設這裏secret爲mysecret,則用go語言實現代碼以下:

package main

import (
 "fmt"
 "encoding/base64"
 "crypto/hmac"
 "crypto/sha256"
 "strings"
)

func main() {

 head1 := `{"typ":"JWT","alg":"HS256"}`

 head1Base64 := base64.StdEncoding.EncodeToString([]byte(head1))

 payload1 := `{"iss":"smallsoup","iat":1528902195,"exp":1528988638,"aud":"www.smallsoup.com","sub":"smallsoup@qq.com","userId":"0418"}`

 payload1Base64 := base64.StdEncoding.EncodeToString([]byte(payload1))

 encodedstring := head1Base64 + "." + payload1Base64

 hash := hmac.New(sha256.New, []byte("mysecret"))
 hash.Write([]byte(encodedstring))

 signature := strings.TrimRight(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "=")

 fmt.Printf(signature)
}

運行結果爲:

fjjbA93FTcE71hz_cyIzCUFYdTdyl9hA0w7Pa0ltduc

最後這個在服務端生成而且要發送給客戶端的 Token 看起來像這樣:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzbWFsbHNvdXAiLCJpYXQiOjE1Mjg5MDIxOTUsImV4cCI6MTUyODk4ODYzOCwiYXVkIjoid3d3LnNtYWxsc291cC5jb20iLCJzdWIiOiJzbWFsbHNvdXBAcXEuY29tIiwidXNlcklkIjoiMDQxOCJ9.fjjbA93FTcE71hz_cyIzCUFYdTdyl9hA0w7Pa0ltduc

實際上https://jwt.io/這個網站提供了這個能力,以及各類語言的生成token和解密token的庫。

image

go語言生成token和解析token:

下面是go語言版的生成token和解析token的案例:

package main

import (
 "github.com/dgrijalva/jwt-go"
 "fmt"
)

func main() {

 hmacSampleSecret := []byte("mysecret")

 // Create a new token object, specifying signing method and the claims
 // you would like it to contain.
 token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
     "iss": "smallsoup",
     "iat": 1528902195,
     "exp": 1528988638,
     "aud": "www.smallsoup.com",
     "sub": "smallsoup@qq.com",
     "userId": "0418",
 })

 // Sign and get the complete encoded token as a string using the secret
 tokenString, err := token.SignedString(hmacSampleSecret)

 fmt.Println(tokenString, err)

 token, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
   // Don't forget to validate the alg is what you expect:
   if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
     return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
   }

   // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
   return hmacSampleSecret, nil
 })

 if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
   fmt.Println(claims)
 } else {
   fmt.Println(err)
 }
}

具體能夠了解github上如下代碼的實現。

go get github.com/dgrijalva/jwt-go


本公衆號免費**提供csdn下載服務,海量IT學習資源,**若是你準備入IT坑,勵志成爲優秀的程序猿,那麼這些資源很適合你,包括但不限於java、go、python、springcloud、elk、嵌入式 、大數據、面試資料、前端 等資源。同時咱們組建了一個技術交流羣,裏面有不少大佬,會不定時分享技術文章,若是你想來一塊兒學習提升,能夠公衆號後臺回覆【2】,免費邀請加技術交流羣互相學習提升,會不按期分享編程IT相關資源。


掃碼關注,精彩內容第一時間推給你

image

原文出處:https://www.cnblogs.com/liabio/p/11696077.html

相關文章
相關標籤/搜索