Go實戰--golang中使用JWT(JSON Web Token)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

生命不止,繼續 go go go !!!

之前寫過關於golang中如何使用cookie的部落格:
實戰–go中使用cookie

今天就來跟大家簡單介紹一下golang中如何使用token,當然是要依賴一下github上的優秀的開源庫了。

首先,要搞明白一個問題,token、cookie、session的區別。

token、cookie、session的區別

Cookie
Cookie總是儲存在用戶端中,按在用戶端中的儲存位置,可分為記憶體Cookie和硬碟Cookie。

記憶體Cookie由瀏覽器維護,儲存在記憶體中,瀏覽器關閉後就消失了,其存在時間是短暫的。硬碟Cookie儲存在硬碟裡,有一個到期時間,除非使用者手工清理或到了到期時間,硬碟Cookie不會被刪除,其存在時間是長期的。所以,按存在時間,可分為非持久Cookie和持久Cookie。

cookie 是一個非常具體的東西,指的就是瀏覽器裡面能永久儲存的一種資料,僅僅是瀏覽器實現的一種資料存放區功能。

cookie由伺服器產生,發送給瀏覽器,瀏覽器把cookie以key-value形式儲存到某個目錄下的文字檔內,下一次請求同一網站時會把該cookie發送給伺服器。由於cookie是存在用戶端上的,所以瀏覽器加入了一些限制確保cookie不會被惡意使用,同時不會佔據太多磁碟空間,所以每個域的cookie數量是有限的。

Session

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

session 也是類似的道理,伺服器要知道當前發請求給自己的是誰。為了做這種區分,伺服器就要給每個用戶端分配不同的“身份標識”,然後用戶端每次向伺服器發請求的時候,都帶上這個“身份標識”,伺服器就知道這個請求來自於誰了。至於用戶端怎麼儲存這個“身份標識”,可以有很多種方式,對於瀏覽器用戶端,大家都預設採用 cookie 的方式。

伺服器使用session把使用者的資訊臨時儲存在了伺服器上,使用者離開網站後session會被銷毀。這種使用者資訊儲存方式相對cookie來說更安全,可是session有一個缺陷:如果web伺服器做了負載平衡,那麼下一個操作請求到了另一台伺服器的時候session會丟失。

Token
token的意思是“令牌”,是使用者身份的驗證方式,最簡單的token組成:uid(使用者唯一的身份標識)、time(目前時間的時間戳記)、sign(簽名,由token的前幾位+鹽以雜湊演算法壓縮成一定長的十六進位字串,可以防止惡意第三方拼接token請求伺服器)。還可以把不變的參數也放進token,避免多次查庫

這裡的token是指SON Web Token:
JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).

使用JWT進行認證
JSON Web Tokens (JWT) are a more modern approach to authentication.

As the web moves to a greater separation between the client and server, JWT provides a wonderful alternative to traditional cookie based authentication models.

JWTs provide a way for clients to authenticate every request without having to maintain a session or repeatedly pass login credentials to the server.

使用者註冊之後, 伺服器產生一個 JWT token返回給瀏覽器, 瀏覽器向伺服器請求資料時將 JWT token 發給伺服器, 伺服器用 signature 中定義的方式解碼
JWT 擷取使用者資訊.

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

JWT應用

上面說了那麼多,接下來就是要coding了。
用到的開源庫:
github.com/codegangsta/negroni
Idiomatic HTTP Middleware for Golang
http的一個中介軟體

github.com/dgrijalva/jwt-go
Golang implementation of JSON Web Tokens (JWT)

github.com/dgrijalva/jwt-go/request

這裡分兩個api,一個是通過login擷取token,然後根據token訪問另一個api。首先看看login是如何產生token的:
當然首先是驗證使用者名稱和密碼,為了節省篇幅這裡只是程式碼片段,完整代碼最後獻上。

    token := jwt.New(jwt.SigningMethodHS256)    claims := make(jwt.MapClaims)    claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()    claims["iat"] = time.Now().Unix()    token.Claims = claims    if err != nil {        w.WriteHeader(http.StatusInternalServerError)        fmt.Fprintln(w, "Error extracting the key")        fatal(err)    }    tokenString, err := token.SignedString([]byte(SecretKey))    if err != nil {        w.WriteHeader(http.StatusInternalServerError)        fmt.Fprintln(w, "Error while signing the token")        fatal(err)    }

接下來就是驗證token的中介軟體了:

    token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor,        func(token *jwt.Token) (interface{}, error) {            return []byte(SecretKey), nil        })    if err == nil {        if token.Valid {            next(w, r)        } else {            w.WriteHeader(http.StatusUnauthorized)            fmt.Fprint(w, "Token is not valid")        }    } else {        w.WriteHeader(http.StatusUnauthorized)        fmt.Fprint(w, "Unauthorized access to this resource")    }

最後完整代碼:

package mainimport (    "encoding/json"    "fmt"    "log"    "net/http"    "strings"    "time"    "github.com/codegangsta/negroni"    "github.com/dgrijalva/jwt-go"    "github.com/dgrijalva/jwt-go/request")const (    SecretKey = "welcome to wangshubo's blog")func fatal(err error) {    if err != nil {        log.Fatal(err)    }}type UserCredentials struct {    Username string `json:"username"`    Password string `json:"password"`}type User struct {    ID       int    `json:"id"`    Name     string `json:"name"`    Username string `json:"username"`    Password string `json:"password"`}type Response struct {    Data string `json:"data"`}type Token struct {    Token string `json:"token"`}func StartServer() {    http.HandleFunc("/login", LoginHandler)    http.Handle("/resource", negroni.New(        negroni.HandlerFunc(ValidateTokenMiddleware),        negroni.Wrap(http.HandlerFunc(ProtectedHandler)),    ))    log.Println("Now listening...")    http.ListenAndServe(":8080", nil)}func main() {    StartServer()}func ProtectedHandler(w http.ResponseWriter, r *http.Request) {    response := Response{"Gained access to protected resource"}    JsonResponse(response, w)}func LoginHandler(w http.ResponseWriter, r *http.Request) {    var user UserCredentials    err := json.NewDecoder(r.Body).Decode(&user)    if err != nil {        w.WriteHeader(http.StatusForbidden)        fmt.Fprint(w, "Error in request")        return    }    if strings.ToLower(user.Username) != "someone" {        if user.Password != "p@ssword" {            w.WriteHeader(http.StatusForbidden)            fmt.Println("Error logging in")            fmt.Fprint(w, "Invalid credentials")            return        }    }    token := jwt.New(jwt.SigningMethodHS256)    claims := make(jwt.MapClaims)    claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()    claims["iat"] = time.Now().Unix()    token.Claims = claims    if err != nil {        w.WriteHeader(http.StatusInternalServerError)        fmt.Fprintln(w, "Error extracting the key")        fatal(err)    }    tokenString, err := token.SignedString([]byte(SecretKey))    if err != nil {        w.WriteHeader(http.StatusInternalServerError)        fmt.Fprintln(w, "Error while signing the token")        fatal(err)    }    response := Token{tokenString}    JsonResponse(response, w)}func ValidateTokenMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {    token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor,        func(token *jwt.Token) (interface{}, error) {            return []byte(SecretKey), nil        })    if err == nil {        if token.Valid {            next(w, r)        } else {            w.WriteHeader(http.StatusUnauthorized)            fmt.Fprint(w, "Token is not valid")        }    } else {        w.WriteHeader(http.StatusUnauthorized)        fmt.Fprint(w, "Unauthorized access to this resource")    }}func JsonResponse(response interface{}, w http.ResponseWriter) {    json, err := json.Marshal(response)    if err != nil {        http.Error(w, err.Error(), http.StatusInternalServerError)        return    }    w.WriteHeader(http.StatusOK)    w.Header().Set("Content-Type", "application/json")    w.Write(json)}

通過postman進行驗證:
login:

根據獲得token進行get請求:

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.