jwt-go 鉴权

33 阅读1分钟

JWT,全称 JSON Web Token,是一种开放标准(RFC 7519),用于安全地在双方之间传递信息。尤其适用于身份验证和授权场景。JWT 的设计允许信息在各方之间安全地、 compactly(紧凑地)传输,因为其自身包含了所有需要的认证信息,从而减少了需要查询数据库或会话存储的需求。

安装

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

定义配置项

type Jwt struct {
    Secret string `mapstructure:"secret" json:"secret" yaml:"secret"`
    JwtTtl int64 `mapstructure:"jwt_ttl" json:"jwt_ttl" yaml:"jwt_ttl"` // token 有效期(秒)
}

config.yaml

jwt:
  secret: 3Bde3BGEbYqtqyEUzW3ry8jKFcaPH17fRmTmqE7MDr05Lwj95uruRKrrkb44TJ4s
  jwt_ttl: 43200

编写颁发 Token 逻辑

services/jwt.go

package services

import (
	"errors"
	"leo-gin/config"
	"time"

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

type jwtService struct {
}

var JwtService = new(jwtService)

// CustomClaims 自定义 Claims
type CustomClaims struct {
	jwt.StandardClaims
}

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

// CreateToken 生成 Token
func (jwtService *jwtService) CreateToken(userId string) (tokenData TokenOutPut, token *jwt.Token, err error) {
	token = jwt.NewWithClaims(
		jwt.SigningMethodHS256,
		CustomClaims{
			StandardClaims: jwt.StandardClaims{
				ExpiresAt: time.Now().Unix() + config.Conf.Jwt.JwtTtl,
				Id:        userId,
				Issuer:    config.Conf.Jwt.GuardName, // 用于在中间件中区分不同客户端颁发的 token,避免 token 跨端使用
				NotBefore: time.Now().Unix() - 1000,
			},
		},
	)

	tokenStr, err := token.SignedString([]byte(config.Conf.Jwt.Secret))

	tokenData = TokenOutPut{
		tokenStr,
		int(config.Conf.Jwt.JwtTtl),
	}
	return
}

// 解析 Token 的方法
func ParseToken(tokenStr string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(config.Conf.Jwt.Secret), nil
	})
	if err != nil {
		return nil, err
	}

	// 校验是否是合法的 Claims
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

// 验证 Token 的方法
func VerifyToken(tokenStr string) int {
	claims, err := ParseToken(tokenStr)
	if err != nil {
		return 1
	}
	if time.Now().Unix() > claims.ExpiresAt-10 {
		return 2
	}
	return 0
}

生成Token

loginReq.User = req
tokenData, _, err := services.JwtService.CreateToken("userId1233456")
if err != nil {
  c.JSON(http.StatusBadRequest, SetOut(400, nil, err.Error()))
  return
}
loginReq.Token = tokenData.AccessToken
loginReq.ExpiresIn = tokenData.ExpiresIn
c.JSON(http.StatusOK, SetOut(200, loginReq))

验证Token中间件

// authRouter := Router.Group("auth", AuthMiddleware())
// 认证中间件
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.GetHeader("Authorization")
		tokenInt := services.VerifyToken(token)
		if tokenInt == 1 {
			c.JSON(http.StatusUnauthorized, controllers.SetOut(401, "Unauthorized"))
			c.Abort()
			return
		} else if tokenInt == 2 {
			c.JSON(http.StatusForbidden, controllers.SetOut(403, "Token expiration"))
			c.Abort()
			return
		}
		c.Next()
	}
}