JWT authentication and golang implementation of JWT demo

Posted by Woodburn2006 on Sat, 06 Nov 2021 20:27:13 +0100

JWT=JSON Web Token is a popular distributed authentication scheme.

Certification scheme

The traditional Cookie/Session authentication scheme is commonly used. The authentication process is as follows:

  1. The user sends username+password to the server;
  2. After authentication, the server stores the login information of the user, such as userId/role/loginTime, etc;
  3. The server returns a session to the user_ ID, write the user's cookie;
  4. Each subsequent request of the user will pass the Cookie to the session_ The ID is returned to the server;
  5. The server received a session_id, find the data reserved in the previous period, so as to obtain the user's identity (userId/role, etc.);

In distributed systems, cross domain authentication is often encountered. For example, website A and website B are associated servers of the same company. Now, users are required to log in automatically as long as they log in to one website and then visit another website. For this problem, common solutions are:

  • Traditional Cookie/Session scheme: centralize and persist sessions and cache them. Each request must be authenticated with the sessionId in the request;
  • JWT scheme: the server does not store session data, the authentication information is stored in the client, and the server only needs session issuance and verification;

JWT(JSON Web Token) is a popular cross domain authentication solution.

JWT certification process

The authentication process of JWT is that the client passes the user name and password into the server. After authentication, the server will generate a JSON object and send it back to the user. The approximate format of the JSON object is as follows:

{
  "full name": "Zhang San",
  "role": "administrators",
  "Expiration time": "2018 At 0:00 on July 1"
}

In the future, when the client communicates with the server, it should bring this JSON object. The server verifies the content of the JSON object and authenticates the user.

In this way, the server does not need to save any session information. The server becomes stateless and has good scalability.

Data structure of JWT:

  • Header: metadata describing JWT, such as signature algorithm;
  • Payload: stores the data actually transmitted. In addition to the four officially defined issuer and expiration time, it can also store private fields, such as userId/userName;
  • Signature: signature of the first two parts to prevent data tampering;

Advantages of JWT: the server is easy to expand. Because the server does not store authentication information and is stateless, it is very conducive to expansion.

Disadvantages of JWT: once a JWT is issued, it is always valid until it expires, unless the server deploys additional logic.

demo of golang JWT

HTTP server usage github.com/gin-gonic/gin.
jwt usage github.com/dgrijalva/jwt-go.

In this demo, the client logs in through POST /login, then obtains the JWT token, and then orders the goods through POST /order by bringing the token in the header.

Generate jwt token

The JWT token is allocated by the server when the user / login:

type AuthClaim struct {
    UserId uint64 `json:"userId"`
    jwt.StandardClaims
}

var secret = []byte("It is my secret")

const TokenExpireDuration = 2 * time.Hour

// Generate JWT token
func GenToken(userId uint64) (string, error) {
    c := AuthClaim{
        UserId: userId,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
            Issuer:    "TEST001",
        },
    }
    //Creates a signed object using the specified signing method
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
    // Use the specified secret signature and obtain the complete encoded string token
    return token.SignedString(secret)
}

Verify jwt token

After the login is successful, when the user requests another request, he / she will bring the JWT token and submit it to the middleware authentication of the server. After the authentication is OK, it will be processed:

func ParseToken(tokenStr string) (*AuthClaim, error) {
    token, err := jwt.ParseWithClaims(tokenStr, &AuthClaim{}, func(tk *jwt.Token) (interface{}, error) {
        return secret, nil
    })
    if err != nil {
        return nil, err
    }
    if claim, ok := token.Claims.(*AuthClaim); ok && token.Valid {
        return claim, nil
    }
    return nil, errors.New("Invalid token ")
}

Implementation of JWTAuthMiddleware

func jwtAuthMiddleware() func(c *gin.Context) {
    return func(c *gin.Context) {
        token := c.Request.Header.Get("token")
        if token == "" {
            c.JSON(http.StatusForbidden, "empty token")
            c.Abort()
            return
        }
        claim, err := ParseToken(token)
        if err != nil {
            c.JSON(http.StatusForbidden, "Invalid token")
            c.Abort()
            return
        }
        c.Set("userId", claim.UserId)
        c.Next()
    }
}

Using JWT Middleware in gin:

r := gin.Default()
r.POST("/login", loginHandler)

api := r.Group("/api")
api.Use(jwtAuthMiddleware())
api.POST("/order", orderHandler)

reference resources:

1.https://mojotv.cn/go/golang-j...
2.https://ruanyifeng.com/blog/2...
3.https://github.com/lwangrabbi...

Topics: Go jwt