生成 token

gin框架使⽤用jwt
鉴权中间件开发
const (
ContextUserIDKey = "userID"
)
var (
ErrorUserNotLogin = errors.New("当前⽤用户未登录")
)

中间件 Middlewears

// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
// 客户端携带Token有三种⽅方式 1.放在请求头 2.放在请求体 3.放在URI
// 这⾥里里假设Token放在Header的中
// 这⾥里里的具体实现⽅方式要依据你的实际业务情况决定
authHeader := c.Request.Header.Get("Auth")
if authHeader ==
"" {
ResponseErrorWithMsg(c, CodeInvalidToken,
c.Abort()
"请求头缺少Auth Token")
return
}
mc, err := utils.ParseToken(authHeader)
if err != nil {
ResponseError(c, CodeInvalidToken)
c.Abort()
return
}
// 将当前请求的username信息保存到请求的上下⽂文c上
c.Set(ContextUserIDKey, mc.UserID)
c.Next() // 后续的处理理函数可以⽤用过c.Get("userID")来获取当前请求的⽤用户信息
}
}

⽣生成access token和refresh token

// GenToken ⽣生成access token 和 refresh token
func GenToken(userID int64) (aToken, rToken string, err error) {
// 创建⼀一个我们⾃自⼰己的声明
c := MyClaims{
userID, // ⾃自定义字段
jwt.StandardClaims{
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
Issuer: "bluebell", // 签发⼈人
},
}
// 加密并获得完整的编码后的字符串串token
aToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256,
c).SignedString(mySecret)
// refresh token 不不需要存任何⾃自定义数据
rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Second * 30).Unix(), // 过期时间
Issuer: "bluebell", // 签发⼈人
}).SignedString(mySecret)
// 使⽤用指定的secret签名并获得完整的编码后的字符串串token
return

}

解析access token

// ParseToken 解析JWT
func ParseToken(tokenString string) (claims *MyClaims, err error) {
// 解析token
var token *jwt.Token
claims = new(MyClaims)
token, err = jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
return
}


if !token.Valid { // 校验token
err = errors.New("invalid token")
}
return
}

refresh token

// RefreshToken 刷新AccessToken
func RefreshToken(aToken, rToken string) (newAToken, newRToken string, err
error) {
// refresh token⽆无效直接返回
if
, err = jwt.Parse(rToken, keyFunc); err != nil {
_
return
}
// 从旧access token中解析出claims数据
var claims MyClaims
, err = jwt.ParseWithClaims(aToken, &claims, keyFunc)
_
v,
:= err.(*jwt.ValidationError)
_
// 当access token是过期错误 并且 refresh token没有过期时就创建⼀一个新的access token
if v.Errors == jwt.ValidationErrorExpired {
return GenToken(claims.UserID)
}
return
}

jwt 的源码文件:

package jwt

import (
	"errors"
	"time"

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

// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims
// jwt包自带的jwt.StandardClaims只包含了官方字段
// 我们这里需要额外记录一个UserID字段,所以要自定义结构体
// 如果想要保存更多信息,都可以添加到这个结构体中
type MyClaims struct {
	UserID uint64 `json:"user_id"`
	jwt.StandardClaims
}

var mySecret = []byte("夏天夏天悄悄过去")

func keyFunc(_ *jwt.Token) (i interface{}, err error) {
	return mySecret, nil
}

const TokenExpireDuration = time.Hour * 24 * 365

// GenToken 生成access token 和 refresh token
func GenToken(userID uint64) (aToken, rToken string, err error) {
	// 创建一个我们自己的声明
	c := MyClaims{
		userID, // 自定义字段
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
			Issuer:    "bluebell",                                 // 签发人
		},
	}
	// 加密并获得完整的编码后的字符串token
	aToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(mySecret)

	// refresh token 不需要存任何自定义数据
	rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
		ExpiresAt: time.Now().Add(time.Second * 30).Unix(), // 过期时间
		Issuer:    "bluebell",                              // 签发人
	}).SignedString(mySecret)
	// 使用指定的secret签名并获得完整的编码后的字符串token
	return
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (claims *MyClaims, err error) {
	// 解析token
	var token *jwt.Token
	claims = new(MyClaims)
	token, err = jwt.ParseWithClaims(tokenString, claims, keyFunc)
	if err != nil {
		return
	}
	if !token.Valid { // 校验token
		err = errors.New("invalid token")
	}
	return
}

// RefreshToken 刷新AccessToken
func RefreshToken(aToken, rToken string) (newAToken, newRToken string, err error) {
	// refresh token无效直接返回
	if _, err = jwt.Parse(rToken, keyFunc); err != nil {
		return
	}

	// 从旧access token中解析出claims数据
	var claims MyClaims
	_, err = jwt.ParseWithClaims(aToken, &claims, keyFunc)
	v, _ := err.(*jwt.ValidationError)

	// 当access token是过期错误 并且 refresh token没有过期时就创建一个新的access token
	if v.Errors == jwt.ValidationErrorExpired {
		return GenToken(claims.UserID)
	}
	return
}

jwt 的中间件

package controller

import (
	"bluebell_backend/pkg/jwt"
	"errors"
	"fmt"
	"strings"

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

const (
	ContextUserIDKey = "userID"
)

var (
	ErrorUserNotLogin = errors.New("当前用户未登录")
)

// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
	return func(c *gin.Context) {
		// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
		// 这里假设Token放在Header的Authorization中,并使用Bearer开头
		// 这里的具体实现方式要依据你的实际业务情况决定
		authHeader := c.Request.Header.Get("Authorization")
		if authHeader == "" {
			ResponseErrorWithMsg(c, CodeInvalidToken, "请求头缺少Auth Token")
			c.Abort()
			return
		}
		// 按空格分割
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			ResponseErrorWithMsg(c, CodeInvalidToken, "Token格式不对")
			c.Abort()
			return
		}
		// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
		mc, err := jwt.ParseToken(parts[1])
		if err != nil {
			fmt.Println(err)
			ResponseError(c, CodeInvalidToken)
			c.Abort()
			return
		}
		// 将当前请求的username信息保存到请求的上下文c上
		c.Set(ContextUserIDKey, mc.UserID)
		c.Next() // 后续的处理函数可以用过c.Get("userID")来获取当前请求的用户信息
	}
}


 

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐