JSON Web Token(JWT)是目前最流行的跨域身份驗證解決方案。簡單說,OAuth 就是一種受權機制。數據的全部者告訴系統,贊成受權第三方應用進入系統,獲取這些數據。系統從而產生一個短時間的進入令(token),用來代替密碼,供第三方應用使用。。git
傳統的受權認證方式,須要持久化session數據,寫入數據庫或文件持久層等,且受權校驗依賴於數據持久層。 這樣的方式,對於結構維護成本大
,實現單點登陸較爲複雜
,且沒有分佈式架構,沒法支持橫向擴展
,風險較大
(若是持久層失敗,整個認證體系都會掛掉)。github
JWT則無須持久化會話數據,是以加密簽名的方式實現了用戶身份認證受權,很好的解決了跨域身份驗證
,分佈式session共享
、單點登陸
和橫向擴展
等問題。web
JWT由三部分組成,即頭部
、負載
與簽名
。Token格式爲 token=頭部+'.'+載荷+'.'+簽名
。算法
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1Nzg0MDQ5OTUsImlhdCI6MTU3ODQwMTM5NX0.waG8rvOZLM2pKDeKg7frMKlV8lAty1Og5LDjrVMJRsI"}
1. Header: 用於說明簽名的加密算法等,下面類型的json通過base64編碼後獲得JWT頭部。數據庫
{ "typ": "JWT", "alg": "HS256" }
2. Payload: 標準定義了7個字段,載荷json通過base64編碼後獲得JWT的載荷。json
{ iss (issuer):簽發人 exp (expiration time):過時時間 sub (subject):主題 aud (audience):受衆 nbf (Not Before):生效時間 iat (Issued At):簽發時間 jti (JWT ID):編號 }
示例:跨域
{ "sub": "1", "iss": "http://localhost:8000/user/sign_up", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667" }
3.Signature: 將頭部和載荷用'.'號鏈接,再加上一串密鑰,通過頭部聲明的加密算法加密後獲得簽名。服務器
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
1. 數據結構session
// A JWT Token. Different fields will be used depending on whether you're // creating or parsing/verifying a token. type Token struct { Raw string // The raw token. Populated when you Parse a token Method SigningMethod // The signing method used or to be used Header map[string]interface{} // (頭部)The first segment of the token Claims Claims // (負載)The second segment of the token Signature string // (簽名)The third segment of the token. Populated when you Parse a token Valid bool // Is the token valid? Populated when you Parse/Verify a token }
// Structured version of Claims Section, as referenced at // https://tools.ietf.org/html/rfc7519#section-4.1 // See examples for how to use this with your own claim types type StandardClaims struct { Id string `json:"jti,omitempty"` //編號 Subject string `json:"sub,omitempty"` //主題 Issuer string `json:"iss,omitempty"` //簽發人 Audience string `json:"aud,omitempty"` //受衆 ExpiresAt int64 `json:"exp,omitempty"` //過時時間 IssuedAt int64 `json:"iat,omitempty"` //簽發時間 NotBefore int64 `json:"nbf,omitempty"` //生效時間 }
2. 生成Token
var( key []byte = []byte("This is secret!") ) // 產生json web token func GenToken() string { claims := &jwt.StandardClaims{ NotBefore: int64(time.Now().Unix()), ExpiresAt: int64(time.Now().Unix() + 1000), Issuer: "Bitch", } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) ss, err := token.SignedString(key) if err != nil { logs.Error(err) return "" } return ss }
3. 校驗Token
// 校驗token是否有效 func CheckToken(token string) bool { _, err := jwt.Parse(token, func(*jwt.Token) (interface{}, error) { return key, nil }) if err != nil { fmt.Println("parase with claims failed.", err) return false } return true }
handler/auth.go
package handler import ( "fmt" "github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go/request" "net/http" ) const ( SecretKey = "ODcyNDYsIMzY0N" ) 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") } }
handler/account.go
package handler import ( "encoding/json" "fmt" "github.com/dgrijalva/jwt-go" "log" "net/http" "strings" "time" ) 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 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) != "admin" { if user.Password != "123456" { w.WriteHeader(http.StatusForbidden) fmt.Fprint(w, "Invalid credentials") return } } // 建立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) } response := Token{tokenString} JsonResponse(response, w) } func FundHandler(w http.ResponseWriter, r *http.Request) { response := Response{"帳戶餘額:1000W"} JsonResponse(response, w) } func JsonResponse(response interface{}, w http.ResponseWriter) { obj, 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(obj) }
main.go
import ( "github.com/codegangsta/negroni" "log" "net/http" "proxy/handler" ) func Server() { http.HandleFunc("/login", handler.LoginHandler) http.Handle("/data", negroni.New( negroni.HandlerFunc(handler.ValidateTokenMiddleware), //中間件 negroni.Wrap(http.HandlerFunc(handler.FundHandler)), )) log.Println("服務已啓動...") http.ListenAndServe(":8080", nil) } func main() { Server() }
客戶端收到服務器返回的 JWT,能夠儲存在 Cookie
裏面,也能夠儲存在 localStorage
。
此後,客戶端每次與服務器通訊,都要帶上這個 JWT。你能夠把它放在 Cookie 裏面自動發送,可是這樣不能跨域
。因此更好的作法是放在HTTP請求的頭信息Authorization
字段裏面(或放在POST 請求的數據體裏面)。
Authorization: Bearer <token>
參考:
http://www.ruanyifeng.com/blog/2019/04/oauth_design.html關於OAuth2.0
http://www.javashuo.com/article/p-fpklooxc-no.html
https://blog.csdn.net/idwtwt/article/details/80865209
https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html