gin框架中使用jwt

生成解析token

如今有很多将身份验证内置到API中的方法 -JSON Web令牌只是其中之一。JSON Web令牌(JWT)作为令牌系统而不是在每次请求时都发送用户名和密码,因此比其他方法(如基本身份验证)具有固有的优势。要了解更多信息,请直接进入jwt.io上的介绍,然后再直接学习。

以下是JWT的实际应用示例。主要有两个部分:提供用户名和密码以获取令牌;并根据请求检查该令牌。

在此示例中,我们使用了两个库,即Go中的JWT实现以及将其用作中间件的方式。

最后,在使用此代码之前,您需要将APP_KEY常量更改为机密(理想情况下,该常量将存储在代码库外部),并改进用户名/密码检查中的内容,TokenHandler以检查不仅仅是myusername/ mypassword组合。

jwt github链接
jwt 官网链接

依赖库:
go get -u github.com/golang-jwt/jwt

生成和解析jwt封装代码:

package tool

import (
	"fmt"
	"github.com/golang-jwt/jwt"
	"time"
)

// 定义签名秘钥
var jwtKey = []byte("www.topgoer.com")

type Claims struct {
	UserId uint
	jwt.StandardClaims
}

// 颁发token
func GenerateToken(userId uint, expireTime time.Time) (tokenStr string) {
	claims := Claims{
		UserId: userId,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(), // 过期时间
			IssuedAt:  time.Now().Unix(), // 签名时间
			Issuer:    "127.0.0.1",       // 签名颁发者
			Subject:   "user token",      // 签名主题
		},
	}
	token := jwt.NewWithClaims(
		jwt.SigningMethodHS256,
		claims,
	)
	tokenStr, err := token.SignedString(jwtKey)
	if err != nil {
		fmt.Println(err)
	}
	return
}

// 解析token
func ParseToken(tokenStr string) (*jwt.Token, *Claims, error) {
	claims := &Claims{}
	token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (i interface{}, err error) {
		return jwtKey, nil
	})
	return token, claims, err
}

gin中中间件实现JWT鉴权

// 中间件实现JWT鉴权
func JWTMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		tokenStr := context.GetHeader("Authorization")
                if tokenString == "" {
                        ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
                        ctx.Abort()
                        return
                }
		token, claims, err := tool.ParseToken(tokenStr)
		if err != nil || !token.Valid {
			fmt.Println(err)
			context.String(401, "权限不足")
			context.Abort()
			return
		}
		context.Set("claims", claims)
	}
}

请求处理中设置和获取JWT

// 将生成的tokeng设置到token中了
func setJWT(context *gin.Context) {
	expireTime := time.Now().Add(60 * time.Second)
	tokenStr := tool.GenerateToken(111, expireTime)
	context.SetCookie("Authorization", tokenStr, 60, "/", "127.0.0.1", false, true)
	context.String(200, "JWT设置成功")
}

// 解析token在中间件中实现了,中间件从header中去读取Authorization获取token,如果成功,中间件会将解析的claims数据放到context上下文中,处理请求中从上下文拿到数据做事情
func getJWT(context *gin.Context) {
	claims, _ := context.Get("claims")
	context.JSON(200, claims)
}

当然中间件也可以从cookie中获取token,代码如下

// 中间件实现JWT鉴权
func JWTMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		tokenStr, _ := context.Cookie("Authorization")
		token, claims, err := tool.ParseToken(tokenStr)
		if err != nil || !token.Valid {
			fmt.Println(err)
			context.String(401, "权限不足")
			context.Abort()
			return
		}
		context.Set("claims", claims)
	}
}

封装jwt组件

  1. request.go文件
package models

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

type CustomClaims struct {
	ID          uint
	NickName    string
	AuthorityId uint  // 角色
	jwt.StandardClaims
}
  1. jwt组件实现创建token,解析校验token,刷新token
package middlewares

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt"
	"mxshop-api/user-web/global"
	"mxshop-api/user-web/models"
	"net/http"
	"time"
)

func JWTAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
		token := c.Request.Header.Get("x-token")
		if token == "" {
			c.JSON(http.StatusUnauthorized, map[string]string{
				"msg":"请登录",
			})
			c.Abort()
			return
		}
		j := NewJWT()
		// parseToken 解析token包含的信息
		claims, err := j.ParseToken(token)
		if err != nil {
			if err == TokenExpired {
				if err == TokenExpired {
					c.JSON(http.StatusUnauthorized, map[string]string{
						"msg":"授权已过期",
					})
					c.Abort()
					return
				}
			}

			c.JSON(http.StatusUnauthorized, "未登陆")
			c.Abort()
			return
		}
		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() *JWT {
	return &JWT{
		[]byte(global.ServerConfig.JWTInfo.SigningKey), //可以设置过期时间
	}
}

// 创建一个token
func (j *JWT) CreateToken(claims models.CustomClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.SigningKey)
}

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

	} else {
		return nil, TokenInvalid

	}

}

// 更新token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &models.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	if err != nil {
		return "", err
	}
	if claims, ok := token.Claims.(*models.CustomClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return j.CreateToken(*claims)
	}
	return "", TokenInvalid
}
  1. 登录成功返回token信息
	// 登录逻辑处理
	// 生成token
	customClaims := models.CustomClaims{
		ID: uint(rsp.Id),
		NickName: rsp.NickName,
		AuthorityId: uint(rsp.Role),
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix(),  // 签名的生效时间
			ExpiresAt: time.Now().Add(time.Second * 15).Unix(),  // 签名的过期时间
			Issuer: "sankuan",  // 签名颁发者
		},
	}
	j := middlewares.NewJWT()
	token, err := j.CreateToken(customClaims)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"msg": "生成token失败"})
		return
	}

	// 登录成功
	c.JSON(http.StatusOK, gin.H{
		"id": rsp.Id,
		"nickname": rsp.NickName,
		"token": token,
		"expired_at": (time.Now().Unix() + 15) * 1000,  // 15秒后过期(毫秒时间戳)
	})

posted @ 2021-12-14 16:02  专职  阅读(633)  评论(0编辑  收藏  举报