Gin框架使用jwt-go配合中间件认证

2022/3/4 6:15:00

本文主要是介绍Gin框架使用jwt-go配合中间件认证,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

参考文档

// 文档
https://github.com/golang-jwt/jwt
https://pkg.go.dev/github.com/golang-jwt/jwt@v3.2.2+incompatible#example-NewWithClaims-CustomClaimsType
https://gin-gonic.com/zh-cn/docs/examples/using-middleware/
https://gin-gonic.com/zh-cn/docs/examples/custom-middleware/

下载

// 下载
go get -u github.com/golang-jwt/jwt

实战

// util/jwt.go
package util

import (
	"gindemo/pkg/setting"
	"time"

	"github.com/golang-jwt/jwt"
)

var jwtSecret = []byte(setting.JwtSecret) //配置文件中自己配置的

// Claims是一些用户信息状态和额外的jwt参数
type Claims struct {
	Username string `json:"username"`
	Password string `json:"password"`
	jwt.StandardClaims
}

// 根据用户的用户名和密码参数token
func GenerateToken(username, password string) (string, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(time.Minute * 15).Unix()

	claims := Claims{
		Username: username,
		Password: password,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime, // 过期时间
			Issuer:    "gindemo",  //指定发行人
		},
	}
	// 该方法内部生成签名字符串,再用于获取完整、已签名的token
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenClaims.SignedString(jwtSecret)
	return token, err
}

// 根据传入的token值获取到Claims对象信息(进而获取其中的用户名和密码)
func ParseToken(token string) (*Claims, error) {
	// 用于解析鉴权的声明,方法内部主要是具体的解码和校验的过程,最终返回*Token
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	})
	if tokenClaims != nil {
		// 从tokenClaims中获取到Claims对象,并使用断言,将该对象转换为我们自己定义的Claims
		// 要传入指针,项目结构体都是用指针传递,节省空间
		if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { // Valid()验证基于时间的声明
			return claims, nil
		}
	}
	return nil, err
}



// 中间件
// middleware/jwt/jwt.go
package jwt

import (
	"gindemo/pkg/e"
	"gindemo/pkg/util"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
)

// 自定义中间件
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 {
                        // 解析token
			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()
	}
}


// models/auth.go
// 数据库查用户
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)
	return auth.ID > 0
}


// routers/api/auth.go
// 用户认证逻辑视图
package api

import (
	"gindemo/models"
	"gindemo/pkg/e"
	"gindemo/pkg/util"
	"log"
	"net/http"

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

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
			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,
	})
}


// routers/router.go
// 添加auth路由
package routers

import (
	"gindemo/middleware/jwt"
	"gindemo/pkg/setting"
	"gindemo/routers/api"
	v1 "gindemo/routers/api/v1"

	"github.com/gin-gonic/gin"
)

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
}


// 测试
http://127.0.0.1:8000/auth?username=test&password=test123456
// 返回数据
{
    "code": 200,
    "data": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwYXNzd29yZCI6InRlc3QxMjM0NTYiLCJleHAiOjE2NDYzMzc0NzMsImlzcyI6ImdpbmRlbW8ifQ.mFGXb6dyYFGIni3joNinfpsNmeDAvvDOFKSfvJ4ss1w"
    },
    "msg": "ok"
}



// 将中间件接入Gin
// routers/router.go
package routers

import (
	"gindemo/middleware/jwt"
	"gindemo/pkg/setting"
	"gindemo/routers/api"
	v1 "gindemo/routers/api/v1"

	"github.com/gin-gonic/gin"
)

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
}


// 以后每次请求的时候先获取Token,然后请求其他url的时候带上token就可以了


这篇关于Gin框架使用jwt-go配合中间件认证的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程