Gin Practice Serialization 5 Use JWT for Identity Verification

  golang, mysql

Use JWT for identity verification

Original address:The Portal of Fans of Fried Fish

In the previous sections, we have basically completed the writing of API’s

However, there are still some very serious problems, for example, our current API can be called at will, which is obviously not perfect and has problems.

Then we adoptjwt-go(GoDoc) to solve this problem simply

Project address:https://github.com/EDDYCJY/go …


Download dependency package

First, we download the jwt-go dependency package

go get -u github.com/dgrijalva/jwt-go

writejwtTool kit

We need to write ajwtThe toolkit, we are inpkginferiorutilNew directoryjwt.go, write file content:

package util

import (
    "time"

    jwt "github.com/dgrijalva/jwt-go"

    "gin-blog/pkg/setting"
)

var jwtSecret = []byte(setting.JwtSecret)

type Claims struct {
    Username string `json:"username"`
    Password string `json:"password"`
    jwt.StandardClaims
}

func GenerateToken(username, password string) (string, error) {
    nowTime := time.Now()
    expireTime := nowTime.Add(3 * time.Hour)

    claims := Claims{
        username,
        password,
        jwt.StandardClaims {
            ExpiresAt : expireTime.Unix(),
            Issuer : "gin-blog",
        },
    }

    tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    token, err := tokenClaims.SignedString(jwtSecret)

    return token, err
}

func ParseToken(token string) (*Claims, error) {
    tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
        return jwtSecret, nil
    })

    if tokenClaims != nil {
        if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
            return claims, nil
        }
    }

    return nil, err
}

In this toolkit, we cover

  • NewWithClaims(method SigningMethod, claims Claims),methodCorresponds toSigningMethodHMAC struct{}A that containsSigningMethodHS256SigningMethodHS384SigningMethodHS512Three kindscrypto.HashProgramme
  • func (t *Token) SignedString(key interface{})This method generates a signature string internally, which is then used to obtain a complete, signedtoken
  • func (p *Parser) ParseWithClaimsThe statement used to parse the authentication,Inside the methodMainly is the concrete decoding and the check process, finally returns*Token
  • func (m MapClaims) Valid()Validate time-based claimsexp, iat, nbfNote that if there is no declaration in the token, it will still be considered valid. And there is no calculation method for time zone deviation

i’ve got itjwtToolkit, next we’re going to write aGinMiddleware of, we are inmiddlewareNew belowjwtDirectory, Newjwt.goFile, write content:

package jwt

import (
    "time"
    "net/http"

    "github.com/gin-gonic/gin"

    "gin-blog/pkg/util"
    "gin-blog/pkg/e"
)

func JWT() gin.HandlerFunc {
    return func(c *gin.Context) {
        var code int
        var data interface{}

        code = e.SUCCESS
        token := c.Query("token")
        if token == "" {
            code = e.INVALID_PARAMS
        } else {
            claims, err := util.ParseToken(token)
            if err != nil {
                code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
            } else if time.Now().Unix() > claims.ExpiresAt {
                code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
            }
        }

        if code != e.SUCCESS {
            c.JSON(http.StatusUnauthorized, gin.H{
                "code" : code,
                "msg" : e.GetMsg(code),
                "data" : data,
            })

            c.Abort()
            return
        }

        c.Next()
    }
}

How to getToken

So how do we call it, we also need to getToken?

1. We will add a new acquisitionTokenAPI for

InmodelsNew belowauth.goFile, write content:

package models

type Auth struct {
    ID int `gorm:"primary_key" json:"id"`
    Username string `json:"username"`
    Password string `json:"password"`
}

func CheckAuth(username, password string) bool {
    var auth Auth
    db.Select("id").Where(Auth{Username : username, Password : password}).First(&auth)
    if auth.ID > 0 {
        return true
    }

    return false
}

InroutersinferiorapiNew directoryauth.goFile, write content:

package api

import (
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/astaxie/beego/validation"

    "gin-blog/pkg/e"
    "gin-blog/pkg/util"
    "gin-blog/models"
)

type auth struct {
    Username string `valid:"Required; MaxSize(50)"`
    Password string `valid:"Required; MaxSize(50)"`
}

func GetAuth(c *gin.Context) {
    username := c.Query("username")
    password := c.Query("password")

    valid := validation.Validation{}
    a := auth{Username: username, Password: password}
    ok, _ := valid.Valid(&a)

    data := make(map[string]interface{})
    code := e.INVALID_PARAMS
    if ok {
        isExist := models.CheckAuth(username, password)
        if isExist {
            token, err := util.GenerateToken(username, password)
            if err != nil {
                code = e.ERROR_AUTH_TOKEN
            } else {
                data["token"] = token
                
                code = e.SUCCESS
            }

        } else {
            code = e.ERROR_AUTH
        }
    } else {
        for _, err := range valid.Errors {
            log.Println(err.Key, err.Message)
        }
    }

    c.JSON(http.StatusOK, gin.H{
        "code" : code,
        "msg" : e.GetMsg(code),
        "data" : data,
    })
}

We open itroutersUnder the directoryrouter.goFile, modify the contents of the file (add a method to obtain token):

package routers

import (
    "github.com/gin-gonic/gin"
    
    "gin-blog/routers/api"
    "gin-blog/routers/api/v1"
    "gin-blog/pkg/setting"
)

func InitRouter() *gin.Engine {
    r := gin.New()

    r.Use(gin.Logger())

    r.Use(gin.Recovery())

    gin.SetMode(setting.RunMode)

    r.GET("/auth", api.GetAuth)

    apiv1 := r.Group("/api/v1")
    {
        ...
    }

    return r
}

VerificationToken

ObtaintokenThis is the end of the API method, let’s test whether it can be used normally.

After restarting the service, use theGETMode accesshttp://127.0.0.1:8000/auth? username=test&password=test123456To see if the return value is correct

{
  "code": 200,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjAwMzcsImlzcyI6Imdpbi1ibG9nIn0.-kK0V9E06qTHOzupQM_gHXAGDB3EJtJS4H5TTCyWwW8"
  },
  "msg": "ok"
}

We have ittokenAPI, also called successfully

Connecting MiddlewareGin

2. Next we will connect the middleware toGinIn the access process of

We open itroutersUnder the directoryrouter.goFile, Modify File Content (Add Reference Package and Middleware Reference)

package routers

import (
    "github.com/gin-gonic/gin"
    
    "gin-blog/routers/api"
    "gin-blog/routers/api/v1"
    "gin-blog/pkg/setting"
    "gin-blog/middleware/jwt"
)

func InitRouter() *gin.Engine {
    r := gin.New()

    r.Use(gin.Logger())

    r.Use(gin.Recovery())

    gin.SetMode(setting.RunMode)

    r.GET("/auth", api.GetAuth)

    apiv1 := r.Group("/api/v1")
    apiv1.Use(jwt.JWT())
    {
        ...
    }

    return r
}

Current directory structure:

gin-blog/
├── conf
│   └── app.ini
├── main.go
├── middleware
│   └── jwt
│       └── jwt.go
├── models
│   ├── article.go
│   ├── auth.go
│   ├── models.go
│   └── tag.go
├── pkg
│   ├── e
│   │   ├── code.go
│   │   └── msg.go
│   ├── setting
│   │   └── setting.go
│   └── util
│       ├── jwt.go
│       └── pagination.go
├── routers
│   ├── api
│   │   ├── auth.go
│   │   └── v1
│   │       ├── article.go
│   │       └── tag.go
│   └── router.go
├── runtime

Here, ourJWTThe compilation is complete!

Verification function

Let’s test it and visit it again.

The correct feedback should be

{
  "code": 400,
  "data": null,
  "msg": "请求参数错误"
}

{
  "code": 20001,
  "data": null,
  "msg": "Token鉴权失败"
}

We need to visithttp://127.0.0.1:8000/auth? username=test&password=test123456Gettoken

{
  "code": 200,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE1MTg3MjQ2OTMsImlzcyI6Imdpbi1ibG9nIn0.KSBY6TeavV_30kfmP7HWLRYKP5TPEDgHtABe9HCsic4"
  },
  "msg": "ok"
}

Reuse includestokenTo access our application API,

Accesshttp://127.0.0.1:8000/api/v1/articles? token=eyJhbGci ...To check the interface return value

{
  "code": 200,
  "data": {
    "lists": [
      {
        "id": 2,
        "created_on": 1518700920,
        "modified_on": 0,
        "tag_id": 1,
        "tag": {
          "id": 1,
          "created_on": 1518684200,
          "modified_on": 0,
          "name": "tag1",
          "created_by": "",
          "modified_by": "",
          "state": 0
        },
        "content": "test-content",
        "created_by": "test-created",
        "modified_by": "",
        "state": 0
      }
    ],
    "total": 1
  },
  "msg": "ok"
}

The return is correct, so far ourjwt-goInGinThe verification in is completed!

This series of catalogues