preface

This chapter will use the JWT-go package to complete the login interface, issue token, and write the JWT middleware to authenticate the token in a unified manner, avoiding repeating the authentication logic in each controller

The installation

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

Defining configuration items

Create the config/jwt.go file and write the configuration

package config

type Jwt struct {
    Secret string `mapstructure:"secret" json:"secret" yaml:"secret"`
    JwtTtl int64 `mapstructure:"jwt_ttl" json:"jwt_ttl" yaml:"jwt_ttl"` // Token validity period (seconds)
}
Copy the code

In config/config.go, add the Jwt attribute

package config

type Configuration struct {
    App App `mapstructure:"app" json:"app" yaml:"app"`
    Log Log `mapstructure:"log" json:"log" yaml:"log"`
    Database Database `mapstructure:"database" json:"database" yaml:"database"`
    Jwt Jwt `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
}
Copy the code

Config. yaml Adds the corresponding configuration

jwt:
  secret: 3Bde3BGEbYqtqyEUzW3ry8jKFcaPH17fRmTmqE7MDr05Lwj95uruRKrrkb44TJ4s
  jwt_ttl: 43200
Copy the code

Write the logic for issuing tokens

Create app/services/jwt.go file and write

package services

import (
    "github.com/dgrijalva/jwt-go"
    "jassue-gin/global"
    "time"
)

type jwtService struct{}var JwtService = new(jwtService)

// All user models that need to issue tokens must implement this interface
type JwtUser interface {
    GetUid() string
}

// CustomClaims CustomClaims
type CustomClaims struct {
    jwt.StandardClaims
}

const (
    TokenType = "bearer"
)

type TokenOutPut struct {
    AccessToken string `json:"access_token"`
    ExpiresIn int `json:"expires_in"`
    TokenType string `json:"token_type"`
}

CreateToken Generates a Token
func (jwtService *jwtService) CreateToken(GuardName string, user JwtUser) (tokenData TokenOutPut, err error, token *jwt.Token) {
    token = jwt.NewWithClaims(
        jwt.SigningMethodHS256,
        CustomClaims{
            StandardClaims: jwt.StandardClaims{
                ExpiresAt: time.Now().Unix() + global.App.Config.Jwt.JwtTtl,
                Id:        user.GetUid(),
                Issuer:    GuardName, // It is used to distinguish tokens issued by different clients in the middleware to avoid cross-terminal use of tokens
                NotBefore: time.Now().Unix() - 1000,
            },
        },
    )

    tokenStr, err := token.SignedString([]byte(global.App.Config.Jwt.Secret))

    tokenData = TokenOutPut{
        tokenStr,
        int(global.App.Config.Jwt.JwtTtl),
        TokenType,
    }
    return
}
Copy the code

The CreateToken method receives a JwtUser instance object. We need to implement the JwtUser interface for the app/ Models /user.go user model. To call CreateToken() to issue the Token

package models

import "strconv"

type User struct {
    ID
    Name string `json:"name" gorm:"not null; Comment: User name "'
    Mobile string `json:"mobile" gorm:"not null; index; Comment: User mobile number "'
    Password string `json:"-" gorm:"not null; default:''; Comment: User password "'
    Timestamps
    SoftDeletes
}

func (user User) GetUid(a) string {
    return strconv.Itoa(int(user.ID.ID))
}
Copy the code

Implementing the login interface

In the app/common/request/user. Go, the new Login validator structure

type Login struct {
    Mobile string `form:"mobile" json:"mobile" binding:"required,mobile"`
    Password string `form:"password" json:"password" binding:"required"`
}

func (login Login) GetMessages(a) ValidatorMessages {
    return ValidatorMessages{
        "mobile.required": "Cell phone number cannot be empty."."mobile.mobile": "Mobile phone number format is incorrect"."password.required": "User password cannot be empty",}}Copy the code

In app/services/user.go, write the Login() Login logic

/ / Login to log in
func (userService *userService) Login(params request.Login) (err error, user *models.User) {
    err = global.App.DB.Where("mobile = ?", params.Mobile).First(&user).Error
    iferr ! =nil| |! utils.BcryptMakeCheck([]byte(params.Password), user.Password) {
        err = errors.New("User name does not exist or password is incorrect")}return
}
Copy the code

New app/controllers/app/auth. Go file, write the Login () into the reference check, and call the UserService and JwtService services, issued Token

package app

import (
    "github.com/gin-gonic/gin"
    "jassue-gin/app/common/request"
    "jassue-gin/app/common/response"
    "jassue-gin/app/services"
)

const GuardName = "app"

func Login(c *gin.Context) {
    var form request.Login
    iferr := c.ShouldBindJSON(&form); err ! =nil {
        response.ValidateFail(c, request.GetErrorMsg(form, err))
        return
    }

    iferr, user := services.UserService.Login(form); err ! =nil {
        response.BusinessFail(c, err.Error())
    } else {
        tokenData, err, _ := services.JwtService.CreateToken(GuardName, user)
        iferr ! =nil {
            response.BusinessFail(c, err.Error())
            return
        }
        response.Success(c, tokenData)
    }
}
Copy the code

In routes/api.go, add a route

router.POST("/auth/login", app.Login)
Copy the code

http://localhost:8888/api/auth/login Postman calls the following figure, successful return to the Token and login successfully

Write JWT authentication middleware

In global/error.go, define the TokenError error

type CustomErrors struct {
    // ...
    TokenError CustomError
}

var Errors = CustomErrors{
    // ...
    TokenError: CustomError{40100."Login authorization invalid"}},Copy the code

In the app/common/response/response. Go, write TokenFail (), used for token authentication failed unified returns

func TokenFail(c *gin.Context) {
    FailByError(c, global.Errors.TokenError)
}
Copy the code

Create app/ Middleware /jwt.go and write

package middleware

import (
    "github.com/dgrijalva/jwt-go"
    "github.com/gin-gonic/gin"
    "jassue-gin/app/common/response"
    "jassue-gin/app/services"
    "jassue-gin/global"
)

func JWTAuth(GuardName string) gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenStr := c.Request.Header.Get("Authorization")
        if tokenStr == "" {
            response.TokenFail(c)
            c.Abort()
            return
        }
        tokenStr = tokenStr[len(services.TokenType)+1:]

        // Token parsing verification
        token, err := jwt.ParseWithClaims(tokenStr, &services.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
            return []byte(global.App.Config.Jwt.Secret), nil
        })
        iferr ! =nil {
            response.TokenFail(c)
            c.Abort()
            return
        }

        claims := token.Claims.(*services.CustomClaims)
        // Verify the Token publisher
        ifclaims.Issuer ! = GuardName { response.TokenFail(c) c.Abort()return
        }

        c.Set("token", token)
        c.Set("id", claims.Id)
    }
}
Copy the code

JWT middleware is used to achieve user information acquisition interface

In Routes /api.go, the JWTAuth middleware is used so that the client needs the correct Token to access the route under the authRouter group

func SetApiGroupRoutes(router *gin.RouterGroup) {
    router.POST("/user/register", app.Register)
    router.POST("/auth/login", app.Login)

    authRouter := router.Group("").Use(middleware.JWTAuth(app.GuardName))
    {
        authRouter.POST("/auth/info", app.Info)
    }
}
Copy the code

In app/services/user.go, write

// GetUserInfo Gets user information
func (userService *userService) GetUserInfo(id string) (err error, user models.User) {
    intId, err := strconv.Atoi(id)
    err = global.App.DB.First(&user, intId).Error
    iferr ! =nil {
        err = errors.New("The data doesn't exist.")}return
}
Copy the code

In APP /controllers/auth.go, write Info() to retrieve user information by verifying tok-recognized user ids with the JWTAuth middleware

func Info(c *gin.Context) {
    err, user := services.UserService.GetUserInfo(c.Keys["id"]. (string))
    iferr ! =nil {
        response.BusinessFail(c, err.Error())
        return
    }
    response.Success(c, user)
}
Copy the code

Will call the Postman, you use the login interface to get Token in the Authorization header, and then call interface at http://localhost:8888/api/auth/info