JWT是JSON Web Token的縮寫,定義了一種簡介自暴寒的方法用於通訊雙方之間以Json對象的形式安全的傳遞信息。由於特定的數字簽名,因此這些通訊的信息可以被校驗和信任。 JWT能夠使用HMAC算法或者RSA的公鑰私鑰對進行簽名。git
讓咱們進一步的解釋下關於JWT的定義:github
JWT由三部分組成,使用.
分隔:golang
因此,JWT最終樣式爲:XXXXX.XXXXX.XXXXX
web
JWT的頭部包含描述該JWT的最基本的信息:token的類型, 如JWT, 以及token使用的加密算法, 如 HMAC SHA256或者RSA.。能夠被表示爲一個JSON對象:算法
{ "typ": "JWT", "alg": "HS256" }
JWT token的第二部分是payload, Payload包含claims. Claims是一些實體(一般指用戶)的狀態信息和其餘元數據。數據庫
{ "iss": "sysu", "iat": 1233458243, "exp": 1448333419, "aud": "sysu-ss", "sub": "sysu-ss", }
iss(issuer 簽發者),是否使用是可選的;
iat(issued at簽發時間),這裏是一個Unix時間戳,是否使用是可選的;
exp(expiration time 過時時間) ,這裏是一個Unix時間戳,是否使用是可選的;
aud(audience 接收方 ),是否使用是可選的;
sub(subject 面向的用戶),是否使用是可選的;json
關於更多有關Payload字段的介紹,請查看JWT官網安全
要建立簽名部分,須要使用通過編碼後的頭部(header)和負載(payload)以及一個密鑰,將header和payload用.
鏈接起來後,使用header中制定的算法進行簽名。
該簽名是用戶驗證JWT的請求發送者以及確保數據信息在傳輸過程當中的消息是未經篡改的。app
我使用的是jwt-go、negroni web中間件以及mux搭建服務。關於這三個庫的使用請自行谷歌。函數
// service/jwt.go var tokens []Token // 做爲全局變量存儲登陸用戶的token,能夠存儲到數據庫中 const TokenName = "SW-TOKEN" const Issuer = "Go-GraphQL-Group" const SecretKey = "StarWars" type Token struct { SW_TOKEN string `json:"SW-TOKEN"` } type jwtCustomClaims struct { jwt.StandardClaims Admin bool `json:"admin"` } func CreateToken(secretKey []byte, issuer string, isAdmin bool) (token Token, err error) { claims := &jwtCustomClaims{ jwt.StandardClaims{ ExpiresAt: int64(time.Now().Add(time.Hour * 1).Unix()), Issuer: issuer, }, isAdmin, } tokenStr, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(secretKey) token = Token{ tokenStr, } return } func ParseToken(tokenStr string, secretKey []byte) (claims jwt.Claims, err error) { var token *jwt.Token token, err = jwt.Parse(tokenStr, func(*jwt.Token) (interface{}, error) { return secretKey, nil }) claims = token.Claims return }
// main.go func NewServer() *negroni.Negroni { router := mux.NewRouter() initRoutes(router) n := negroni.Classic() // negroni.Classic() 返回帶有默認中間件的Negroni實例指針 n.UseHandler(router) // 將router中http.Handler加入到negroni的中間件棧中 return n } func initRoutes(router *mux.Router) { router.HandleFunc("/login", service.LoginHandler).Methods("POST") // 使用中間件進行token認證 router.Use(service.TokenMiddleware) // query服務 router.HandleFunc("/query", handler.GraphQL(GraphQL_Service.NewExecutableSchema(GraphQL_Service.Config{Resolvers: &GraphQL_Service.Resolver{}}))) // 退出路由 router.HandleFunc("/logout", service.LogoutHandler).Methods("POST", "GET") }
func LoginHandler(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() fmt.Println(r.Form.Get("username")) if err != nil { w.WriteHeader(http.StatusForbidden) fmt.Fprint(w, "Error in request") return } // 使用固定的username和password作測試 if strings.ToLower(r.Form.Get("username")) != "admin" || r.Form.Get("password") != "password" { w.WriteHeader(http.StatusForbidden) fmt.Println("Error logging in") fmt.Fprint(w, "Invalid credentials") return } token, err := CreateToken([]byte(SecretKey), Issuer, false) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, "Error extracting the key") log.Fatal(err) } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") tokenBytes, err := json.Marshal(token) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, "Error marshal the token") log.Fatal(err) } tokens = append(tokens, token) w.Write(tokenBytes) }
func TokenMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 不是login的請求須要進行token認證 if r.RequestURI[1:] != "login" { /* // token位於Authorization中,用此方法 token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor, func(token *jwt.Token) (interface{}, error) { return []byte(SecretKey), nil }) */ tokenStr := "" for k, v := range r.Header { if strings.ToUpper(k) == TokenName { tokenStr = v[0] break } } validToken := false for _, token := range tokens { if token.SW_TOKEN == tokenStr { validToken = true } } if validToken { ctx := context.WithValue(r.Context(), TokenName, tokenStr) next.ServeHTTP(w, r.WithContext(ctx)) } else { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Unauthorized access to this resource")) //fmt.Fprint(w, "Unauthorized access to this resource") } } else { next.ServeHTTP(w, r) } }) }
func LogoutHandler(w http.ResponseWriter, r *http.Request) { tokenStr := "" for k, v := range r.Header { if strings.ToUpper(k) == TokenName { tokenStr = v[0] break } } for i, token := range tokens { if token.SW_TOKEN == tokenStr { tokens = append(tokens[:i], tokens[i+1:]...) break } } w.Write([]byte("logout")) }
// main.go func main() { port := os.Getenv("PORT") if port == "" { port = defaultPort } server := NewServer() server.Run(":" + port) log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) }
代碼源地址:Github