JWT realizes authorization authentication

Keywords: Go JSON Session Unix github

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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

Posted by w4designs on Wed, 08 Jan 2020 08:10:31 -0800