preface

This is the 8th day of my participation in the Challenge. Hi, I am The composer Zhong Yang. In the last article, we learned about Redis access and the development of graphic Captcha interface

1. Introduction

I learned about middleware in the Zap logging Management section and wrote a logging middleware that logs HTTP exceptions

Here are a few articles that speak very well:

Go Web Lightweight Framework Gin Learning Series: Details on Middleware usage

Gin Framework Series 03: Understanding Middleware in a different way

Middleware icon: My understanding of middleware:

1. Similar to koA’s Onion model, Djangos hook function, front-end life cycle

2. Do some logic before or after requesting an interface

Middleware several keywords:

1. C.ext () goes to the next middleware

1. C. aport () interrupts middleware (return cannot interrupt middleware calls)

2. JWT is introduced

JWT’s official website: JWT library = JWT library = JWT

 go get github.com/dgrijalva/jwt-go
Copy the code

JSON Web Token consists of three parts, separated by dots (.). The connection. The three parts are:

  • Header
  • Payload
  • Signature

(2) Stateless and scalable: Tokens are stored on the client. Completely stateless and extensible. Our load balancer can pass users to any server because there is no state or session information anywhere. Security: Tokens are sent on every request. Also, since no cookies are sent, it helps prevent CSRF attacks. Even if you store tokens in a client Cookie in your implementation, the Cookie is a storage mechanism, not an authentication mechanism. There is no session-based information to operate on because we have no sessions!

Agreement with the front end:

  1. When a login request is made, the backend passes the token to the front-end,Front-end save token
  2. The front end adds a key to the HTTP request headerX-token request headerAnd the value for the token

2. JWT middleware development

(1). Add the token configuration

2.1.1 Add it to settings-dev.yaml to generate the address of the key

jwt:
  key: "EYsnfKMf5XWk87LASEs28Dj5ZqGkSerH"
Copy the code

2.1.2 Add the configured struct in config/config.go

(2). Remove the forms/user. Go the PasswordLoginForm. Mobile custom validation in the tag in the UserName

Ps: Verify that username must be a mobile phone number

(3). Write JWT middleware and related code

3.2.1 Write in Middlewares/JWT (while copying, please read the comments and code logic ~)

package middlewares
import (
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/fatih/color"
	"github.com/gin-gonic/gin"
	"go_gin/Response"
	"go_gin/global"
	"net/http"
	"time"
)

type CustomClaims struct {
	ID          uint   //
	NickName    string //
	AuthorityId uint   //
	jwt.StandardClaims
}

func JWTAuth(a) gin.HandlerFunc {
	return func(c *gin.Context) {
		In this case, the front-end needs to store the token in the cookie or local localSstorage. However, the expiration time needs to be negotiated with the back-end to renew the token or re-log in
		token := c.Request.Header.Get("x-token")
		color.Yellow(token)
		if token == "" {
			Response.Err(c, http.StatusUnauthorized, 401."Please log in"."")
			c.Abort()
			return
		}
		j := NewJWT()
		// parseToken Parses the information contained in the token
		claims, err := j.ParseToken(token)
		iferr ! =nil {
			if err == TokenExpired {
				if err == TokenExpired {
					Response.Err(c, http.StatusUnauthorized, 401."Authorization has expired."."")
					c.Abort()
					return
				}
			}
			Response.Err(c, http.StatusUnauthorized, 401."No landing"."")
			c.Abort()
			return
		}
		fmt.Println(c)
		// Gin's context records claims and userId values
		c.Set("claims", claims)
		c.Set("userId", claims.ID)
		c.Next()
	}
}

type JWT struct {
	SigningKey []byte
}

var (
	TokenExpired     = errors.New("Token is expired")
	TokenNotValidYet = errors.New("Token not active yet")
	TokenMalformed   = errors.New("That's not even a token")
	TokenInvalid     = errors.New("Couldn't handle this token:"))func NewJWT(a) *JWT {
	return &JWT{
		[]byte(global.Settings.JWTKey.SigningKey),
	}
}

// Create a token
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.SigningKey)
}

/ / token
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return j.SigningKey, nil
	})
	iferr ! =nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			ifve.Errors&jwt.ValidationErrorMalformed ! =0 {
				return nil, TokenMalformed
			} else ifve.Errors&jwt.ValidationErrorExpired ! =0 {
				// Token is expired
				return nil, TokenExpired
			} else ifve.Errors&jwt.ValidationErrorNotValidYet ! =0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}
	iftoken ! =nil {
		if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
			return claims, nil
		}
		return nil, TokenInvalid

	} else {
		return nil, TokenInvalid

	}

}

/ / update the token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func(a) time.Time {
		return time.Unix(0.0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	iferr ! =nil {
		return "", err
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return j.CreateToken(*claims)
	}
	return "", TokenInvalid
}
Copy the code

3.2.2. Add createToken function in utils/createToken.go

package utils

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"go_gin/Response"
	"go_gin/middlewares"
	"time"
)

func CreateToken(c *gin.Context, Id int, NickName string, Role int) string {
	// Generate token information
	j := middlewares.NewJWT()
	claims := middlewares.CustomClaims{
		ID:          uint(Id),
		NickName:    NickName,
		AuthorityId: uint(Role),
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix(),
			// TODO Sets the token expiration time
			ExpiresAt: time.Now().Unix() + 60*60*24*30.// Token --> expires in 30 days
			Issuer:    "test",}}/ / token is generated
	token, err := j.CreateToken(claims)
	iferr ! =nil {
		Response.Success(c, 401."Token generation failed, try again"."test")
		return ""
	}
	return token
}
Copy the code

4. Improve the login interface

(1) Add the UsernameFindUserInfo function in dao/ Userdao.go

var user models.User
// UsernameFindUserInfo Finds user information through username
func FindUserInfo(username string, password string) (*models.User, bool) {
	// Query the user
	rows := global.DB.Where(&models.User{NickName: username, Password: password}).Find(&user)
	fmt.Println(&user)
	if rows.RowsAffected < 1 {
		return &user, false
	}
	return &user, true
}
Copy the code

(2) Rewrite the PasswordLogin logic in controller/user.go

Ps: Check whether the user exists through username, and return the user information

A few points to note:

1. The verification of the graph verification code is temporarily blocked

2.HandleUserModelToMap (controller/user. Go) code is

func HandleUserModelToMap(user *models.User) map[string]interface{} {
  birthday := ""
  if user.Birthday == nil {
  	birthday = ""
  } else {
  	birthday = user.Birthday.Format("2006-01-02")
  }
  userItemMap := map[string]interface{} {"id":        user.ID,
  	"nick_name": user.NickName,
  	"head_url":  user.HeadUrl,
  	"birthday":  birthday,
  	"address":   user.Address,
  	"desc":      user.Desc,
  	"gender":    user.Gender,
  	"role":      user.Role,
  	"mobile":    user.Mobile,
  }
  return userItemMap
}
Copy the code

3. Test JWT middleware and login interface tests

(1). Login interface test

Postman open:http://127.0.0.1:8022/v1/user/list?page=2&pageSize=10 Ps: The token information is correctly returned during login

(2). Test JWT middleware validation

Add JWT middleware validation to router/user.go:Ps: Middleware is indeed in the middle 🤪🤪🤪

Test userList interface result:

Postman open:http://127.0.0.1:8022/v1/user/list?page=2&pageSize=10 Ps: you can test the successful request after the token is added

At this point, the middleware of JWT is added successfully. Continue to develop other middleware.

If you found any of these articles useful, please give them a thumbs up and leave a comment