Json Web Token (JWT),是一個很是輕巧的規範,這個規範容許在網絡應用環境間客戶端和服務器間較安全的傳遞信息。該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源。html
在web應用中,咱們提供的API接口,經過GET或者POST方式調用,在調用過程當中,就存在着接口認證及數據的安全性問題。例如以下問題:前端
一、請求來自哪裏,身份是否合法?java
二、請求參數是否被篡改?python
三、客戶端請求的惟一性,是否爲重複請求攻擊(RepeatAttack)?git
在傳統的web應用中,服務端成功相應請求者,返回正常的response依賴於服務端經過一種存儲機制把每一個用戶通過認證以後的會話信息(session)記錄服務器端,通常記錄到內存、磁盤、數據庫中,這種方式在請求量和用戶量增多的時候無疑會增大服務端的開銷;若是是記錄在內存中,那每次請求都分發登到該機器上才能受權獲取資源,那在分佈式系統中就存在着問題;由於是基於Cookie的,若是Cookie被截獲,攻擊者會盜用身份信息進行發送惡意請求,也就是「跨站請求僞造」(CSRF)。github
客戶端用用戶名和密碼通過服務器認證以後,服務器會簽發一個token返回給客戶端,客戶端存儲token(通常存在請求頭裏),而且在以後的請求裏附帶此token,服務器每次會解簽名token,驗證經過則返回資源。另外服務端要支持CORS跨來源資源共享)策略,服務器處理完請求以後,會再返回結果中加上Access-Control-Allow-Origin。web
token是接口的令牌,比如去衙門辦事,「衙門口朝南開,有理無錢莫進來」。沒有令牌就別想辦事。token的驗證方法不少,也生成了不少標準,jwt就是一種基於json的RFC 7519。該標準由三部分組成:面試
header算法
payloadspring
signature
header和payload通過base64編碼後用點拼接起來。signature是把header和payload編碼和拼接後通過加密算法加密,加密時還要一個密碼,這個密碼保存在服務器端。大體示意圖以下:
Header:
head由兩部分組成,一個是token類型,一個是使用的算法,以下類型爲jwt,使用的算法是HS256。固然,還有HS38四、HS512算法。
{ "typ": "JWT", "alg": "HS256" }
將以上json進行base64編碼,固然編碼前將json去格式化,如圖:
生成的編碼爲:
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
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的庫。
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相關資源。
掃碼關注,精彩內容第一時間推給你
原文出處:https://www.cnblogs.com/liabio/p/11696077.html