Catalog
1, What is JWT
JSON Web Token (JWT) is the most popular cross domain authentication solution. In short, OAuth is a kind of authorization mechanism. The owner of the data told the system that he agreed to authorize the third-party application to enter the system and obtain the data. The system then generates a short-term token to replace the password for use by third-party applications..
The traditional authorization authentication method needs to persist session data, write to database or file persistence layer, and authorization verification depends on data persistence layer. In this way, the cost of structure maintenance is large, the implementation of single sign on is complex, and there is no distributed architecture, which can not support horizontal expansion, and the risk is large (if the persistence layer fails, the whole authentication system will be hung up).
JWT does not need to hold session data for a long time. It implements user authentication and authorization by means of encrypted signature. It solves the problems of cross domain authentication, distributed session sharing, single sign on and horizontal expansion.
2, JWT Standard Specification
JWT consists of three parts: header, payload and signature. The token format is token = header + '.' + payload + '.' + signature.
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1Nzg0MDQ5OTUsImlhdCI6MTU3ODQwMTM5NX0.waG8rvOZLM2pKDeKg7frMKlV8lAty1Og5LDjrVMJRsI"}
1. Header: used to describe the encryption algorithm of signature. The following types of json are encoded by base64 to get JWT header.
{ "typ": "JWT", "alg": "HS256" }
2. Payload: seven fields are defined in the standard. The payload json is encoded by base64 to get the payload of JWT.
{ iss (issuer): issuer exp (expiration time): expiration time Subject: subject aud (audience) nbf (Not Before): effective time iat (Issued At): issuing time jti (JWT ID): No }
Example:
{ "sub": "1", "iss": "http://localhost:8000/user/sign_up", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667" }
3.Signature: connect the header and the payload with '.', and then add a string of keys. After the encryption algorithm declared by the header is encrypted, the signature is obtained.
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
3, Analysis of core code
1. Data structure
// 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 // (signature) 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"` //number Subject string `json:"sub,omitempty"` //theme Issuer string `json:"iss,omitempty"` //Issuer Audience string `json:"aud,omitempty"` //Audience ExpiresAt int64 `json:"exp,omitempty"` //Expiration date IssuedAt int64 `json:"iat,omitempty"` //Time filed NotBefore int64 `json:"nbf,omitempty"` //entry-into-force time }
2. Generate Token
var( key []byte = []byte("This is secret!") ) // Generate 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. Verification Token
// Verify whether the token is valid 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 }
4, Login authorization example
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 } } // Create 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{"Account balance: 1000 W"} 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), //middleware negroni.Wrap(http.HandlerFunc(handler.FundHandler)), )) log.Println("Service started...") http.ListenAndServe(":8080", nil) } func main() { Server() }
5, JWT usage
The client receives the JWT returned by the server, which can be stored in the Cookie or localStorage.
After that, every time the client communicates with the server, it will bring the JWT. You can put it in a Cookie and send it automatically, but it can't cross domains. So it is better to put it in the Authorization field of HTTP request header information (or in the data body of POST request).
Authorization: Bearer <token>
6, JWT considerations
- JWT is not encrypted by default, but it can be encrypted. After the original token is generated, it can be encrypted again using the changed token.
- JWT can be used not only for authentication, but also for information exchange. Making good use of JWT can help reduce the number of times the server requests the database.
- The biggest disadvantage of JWT is that the server does not save the session state. Once JWT is issued, it will remain valid for the validity period.
- The validity period of JWT should not be set too long, so once the information is leaked, anyone can get all the permissions of the token.
- In order to reduce the risk of JWT data theft and theft, JWT recommends using encrypted HTTPS protocol for transmission.
Reference resources:
http://www.ruanyifeng.com/blog/2019/04/oauth_design.html About OAuth2.0
https://blog.csdn.net/wangshubo1989/article/details/74529333
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