Go語言入門篇-jwt(json web token)權限驗證


一。token、cookie、session的區別

1。cookie

Cookie總是保存在客戶端中,按在客戶端中的存儲位置,可分為內存Cookie和硬盤Cookie。

內存Cookie由瀏覽器維護,保存在內存中,瀏覽器關閉后就消失了,其存在時間是短暫的。

硬盤Cookie保存在硬盤里,有一個過期時間,除非用戶手工清理或到了過期時間,硬盤Cookie不會被刪除,其存在時間是長期的。

所以,按存在時間,可分為非持久Cookie和持久Cookie。 cookie 是一個非常具體的東西,指的就是瀏覽器里面能永久存儲的一種數據,僅僅是瀏覽器實現的一種數據存儲功能。 cookie由服務器生成,發送給瀏覽器,瀏覽器把cookie以key
-value形式保存到某個目錄下的文本文件內,下一次請求同一網站時會把該cookie發送給服務器。

由於cookie是存在客戶端上的,所以瀏覽器加入了一些限制確保cookie不會被惡意使用,同時不會占據太多磁盤空間,所以每個域的cookie數量是有限的。

2。session

session 從字面上講,就是會話。這個就類似於你和一個人交談,你怎么知道當前和你交談的是張三而不是李四呢?對方肯定有某種特征(長相等)表明他就是張三。

session 也是類似的道理,服務器要知道當前發請求給自己的是誰。為了做這種區分,服務器就要給每個客戶端分配不同的“身份標識”,

然后客戶端每次向服務器發請求的時候,都帶上這個“身份標識”,服務器就知道這個請求來自於誰了。至於客戶端怎么保存這個“身份標識”,

可以有很多種方式,對於瀏覽器客戶端,大家都默認采用 cookie 的方式。 服務器使用session把用戶的信息臨時保存在了服務器上,用戶離開網站后session會被銷毀。這種用戶信息存儲方式相對cookie來說更安全,

可是session有一個缺陷:如果web服務器做了負載均衡,那么下一個操作請求到了另一台服務器的時候session會丟失。

3。token

token的意思是“令牌”,是用戶身份的驗證方式.

最簡單的token組成:

【1】uid(用戶唯一的身份標識)
【2】time(當前時間的時間戳)
【3】sign(簽名,由token的前幾位+鹽以哈希算法壓縮成一定長的十六進制字符串,
可以防止惡意第三方拼接token請求服務器)。還可以把不變的參數也放進token,避免多次查庫 這里的token是指SON Web Token

用戶注冊之后, 服務器生成一個 JWT token返回給瀏覽器, 瀏覽器向服務器請求數據時將 JWT token 發給服務器,

服務器用 signature 中定義的方式解碼 

WT 獲取用戶信息.

一個 JWT token包含3部分: 
【1】header: 告訴我們使用的算法和 token 類型 
【2】Payload: 必須使用 sub key 來指定用戶 ID, 還可以包括其他信息比如 email, username 等. 
【3】Signature: 用來保證 JWT 的真實性. 可以使用不同算法 

二。jwt(json web token)權限驗證

完整代碼:

  1 package main
  2 
  3 import (
  4     "log"
  5     "net/http"
  6     "github.com/codegangsta/negroni"
  7     "encoding/json"
  8     "fmt"
  9     "strings"
 10     "github.com/dgrijalva/jwt-go"
 11     "time"
 12     "github.com/dgrijalva/jwt-go/request"
 13 )
 14 
 15 /**
 16 說明:
 17 客戶端通過在request對象header里添加token參數,發送到服務端。
 18 服務端再拿出token進行比對。
 19 token的第一次產生是發生在login檢查賬戶存在並且正確之后,為該用戶賦予一塊令牌(加密字符串)
 20 並將token放入response的header里。客戶端登陸成功后,從response里取出token。並在以后操作request請求。
 21 都保持在header里添加該段令牌,令牌有效期失效后,只有重新login,才能獲取新的令牌。
 22 */
 23 const (
 24     //SecretKey = "welcome to wangshubo's blog"
 25     SecretKey = "I have login"
 26 )
 27 
 28 func fatal(err error) {
 29     if err != nil {
 30         log.Fatal(err)
 31     }
 32 }
 33 
 34 type UserCredentials struct {
 35     Username string `json:"username"`
 36     Password string `json:"password"`
 37 }
 38 
 39 type User struct {
 40     ID       int    `json:"id"`
 41     Name     string `json:"name"`
 42     Username string `json:"username"`
 43     Password string `json:"password"`
 44 }
 45 
 46 type Response struct {
 47     Data string `json:"data"`
 48 }
 49 
 50 type Token struct {
 51     Token string `json:"token"`
 52 }
 53 
 54 func StartServer() {
 55 
 56     http.HandleFunc("/login", LoginHandler)
 57 
 58     http.Handle("/resource", negroni.New(
 59         negroni.HandlerFunc(ValidateTokenMiddleware),
 60         negroni.Wrap(http.HandlerFunc(ProtectedHandler)),
 61     ))
 62 
 63     log.Println("Now listening...")
 64     http.ListenAndServe(":8080", nil)
 65 }
 66 
 67 func main() {
 68     StartServer()
 69 }
 70 
 71 func ProtectedHandler(w http.ResponseWriter, r *http.Request) {
 72 
 73     response := Response{"Gained access to protected resource !"}
 74     JsonResponse(response, w)
 75 
 76 }
 77 
 78 //服務端生成token,並放入到response的header
 79 /**
 80 JWT由三部份組成:
 81 * Header:頭部 (對應:Header)
 82 * Claims:聲明 (對應:Payload)
 83 * Signature:簽名 (對應:Signature)
 84 */
 85 func LoginHandler(w http.ResponseWriter, r *http.Request) {
 86 
 87     var u *User=new(User)
 88 
 89     var user UserCredentials
 90 
 91     err := json.NewDecoder(r.Body).Decode(&user)
 92 
 93     if err != nil {
 94         w.WriteHeader(http.StatusForbidden)
 95         fmt.Fprint(w, "Error in request")
 96         return
 97     }
 98 
 99     //驗證是身份:若用戶是someone,則生成token
100     if strings.ToLower(user.Username) != "someone" {
101         if user.Password != "p@ssword" {
102             w.WriteHeader(http.StatusForbidden)
103             fmt.Println("Error logging in")
104             fmt.Fprint(w, "Invalid credentials")
105             return
106         }
107     }
108 
109     //1。生成token
110     token := jwt.New(jwt.SigningMethodHS256)
111     claims := make(jwt.MapClaims)
112     //2。添加令牌關鍵信息
113     //添加令牌期限
114     claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
115     claims["iat"] = time.Now().Unix()
116     claims["id"]=u.ID
117     claims["userName"]=u.Username
118     claims["password"]=u.Password
119     token.Claims = claims
120 
121     fmt.Println(claims)
122 
123     if err != nil {
124         w.WriteHeader(http.StatusInternalServerError)
125         fmt.Fprintln(w, "Error extracting the key")
126         fatal(err)
127     }
128 
129     //獲取令牌
130     tokenString, err := token.SignedString([]byte(SecretKey))
131     if err != nil {
132         w.WriteHeader(http.StatusInternalServerError)
133         fmt.Fprintln(w, "Error while signing the token")
134         fatal(err)
135     }
136 
137     //2。將生成的token放入到header
138     response := Token{tokenString}
139     JsonResponse(response, w)
140 
141 }
142 
143 //驗證Token
144 func ValidateTokenMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
145     token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor,
146         func(token *jwt.Token) (interface{}, error) {
147             return []byte(SecretKey), nil
148         })
149 
150     if err == nil {
151         if token.Valid {
152             next(w, r)
153         } else {
154             w.WriteHeader(http.StatusUnauthorized)
155             fmt.Fprint(w, "Token is not valid")
156         }
157     } else {
158         w.WriteHeader(http.StatusUnauthorized)
159         fmt.Fprint(w, "Unauthorized access to this resource")
160     }
161 
162 }
163 
164 func JsonResponse(response interface{}, w http.ResponseWriter) {
165     json, err := json.Marshal(response)
166     if err != nil {
167         http.Error(w, err.Error(), http.StatusInternalServerError)
168         return
169     }
170     w.WriteHeader(http.StatusOK)
171     w.Header().Set("Content-Type", "application/json")
172     w.Write(json)
173 }
View Code

通過postman進行驗證:

Step1:發送登錄請求

Step2:使用服務端返回客戶端的token去驗證有效性(根據獲得的token進行get請求)

 

參考博客:https://blog.csdn.net/wangshubo1989/article/details/74529333


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM