go gin框架jwt登录鉴权

鉴权类及方法

package init
import (
	"errors"
	"fmt"
	"gin/global"
	"gin/utils"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt"
	"net/http"
	"time"
)

type JWT struct {
	// 声明签名信息
	Secret []byte
}

// NewJWT 初始化JWT对象
func NewJWT() *JWT {
	return &JWT{
		Secret: []byte(global.Settings.JwtConfig.Secret),
	}
}

// CustomClaims 自定义有效载荷
type CustomClaims struct {
	Name               string `json:"name"`
	UserId             string `json:"id"`
	jwt.StandardClaims        // StandardClaims结构体实现了Claims接口(Valid()函数)
}

// CreateToken 调用jwt-go库生成token
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
	// 指定编码算法为jwt.SigningMethodHS256
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 返回一个token结构体指针
	return token.SignedString(j.Secret)
}

// ParserToken token解码
func (j *JWT) ParserToken(tokenString string) (*CustomClaims, error) {
	// 输入用户自定义的Claims结构体对象,token,以及自定义函数来解析token字符串为jwt的Token结构体指针
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.Secret, nil
	})
	if err != nil {
		var ve *jwt.ValidationError
		if errors.As(err, &ve) { // jwt.ValidationError:是一个无效token的错误结构
			if ve.Errors&jwt.ValidationErrorMalformed != 0 { // ValidationErrorMalformed是一个uint常量,表示token不可用
				return nil, fmt.Errorf("token不可用")
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 { // ValidationErrorExpired表示Token过期
				return nil, fmt.Errorf("token过期")
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { // ValidationErrorNotValidYet表示无效token
				return nil, fmt.Errorf("无效的token")
			} else {
				return nil, fmt.Errorf("token不可用")
			}
		}
		return nil, err
	}
	// 将token中的claims信息解析出来并断言成用户自定义的有效载荷结构
	claims, ok := token.Claims.(*CustomClaims)
	if ok && token.Valid {
		return claims, nil
	}
	return nil, fmt.Errorf("token不可用")
}

// JWTAuth 定义一个JWTAuth的中间件
func JWTAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 通过http header中的token解析来认证
		token := c.Request.Header.Get("token")
		if token == "" {
			utils.ReturnError(c, http.StatusForbidden, "请求未携带token,无权访问!")
			c.Abort() // Abort 函数在被调用的函数中阻止后续中间件的执行(这里都没有携带token,后续就不用执行了)
			return
		}
		j := NewJWT()                       // 初始化一个JWT对象实例,并根据结构体方法来解析token
		claims, err := j.ParserToken(token) // 解析token中包含的相关信息(有效载荷)
		if err != nil {
			utils.ReturnError(c, http.StatusForbidden, err.Error())
			c.Abort()
			return
		}
		if redistoken, _ := global.Redis.Get(claims.UserId).Result(); redistoken != "" {
			if redistoken == token {
				if claims.Name != global.Settings.JwtConfig.Super {
					url := c.Request.URL.RequestURI()
					var total int64
					//鉴权 具体逻辑代码可根据自己的改写
					global.DB.Raw("select * from v_user_func where url=? and username=?", url, claims.UserId).Scan(&total)
					if total == 0 {
						utils.ReturnError(c, http.StatusForbidden, "您没有该权限")
					}
				}
				//若在到期之前时间段请求可刷新token
				if claims.ExpiresAt-time.Now().Unix() < global.Settings.JwtConfig.RefreshTokenPeriod {
					c.Header("refresh_token", GetToken(claims.Name, claims.UserId))
				} 
				// 将解析后的有效载荷claims重新写入gin.Context引用对象中(gin的上下文)
				c.Set("claims", claims)
			} else {
				utils.ReturnError(c, http.StatusForbidden, "token已刷新,请使用最新token")
				c.Abort()
				return
			}
		} else {
			utils.ReturnError(c, http.StatusForbidden, "token不可用")
			c.Abort()
			return
		}
	}
}

func GetToken(use string, userid string) string {
	j := NewJWT()           // 构造SignKey: 签名和解签名需要使用一个值
	claims := CustomClaims{ // 构造用户claims信息(负载)
		Name:   use,
		UserId: userid,
		StandardClaims: jwt.StandardClaims{
			NotBefore: int64(time.Now().Unix()),                                    // 签名生效时间秒
			ExpiresAt: int64(time.Now().Unix() + global.Settings.JwtConfig.Expire), // 签名过期时间秒
			Issuer:    global.Settings.JwtConfig.Secret,                            // 签名颁发者
		},
	}
	token, err := j.CreateToken(claims) // 根据claims生成token对象
	if err != nil {
		panic(err)
	}
	//存储具体使用看个人的逻辑
	//存redis
	result, err := global.Redis.Set(userid, token, time.Duration(global.Settings.JwtConfig.Expire)*time.Second).Result()
	fmt.Println(result)
	return token
}

使用

package router
import (
	"gin/controller"
	"gin/init"
	"github.com/gin-gonic/gin"
)
func Router() *gin.Engine {
all := r.Group("").Use(init.JWTAuth())
{
	all.GET("/test",controller.TestController{}.Test)
}

读取yaml文件去看这篇

jwt:
  secret: 'xxx'
  #刷新时间 秒
  refresh_token_period: 1800
  #过期时间 秒
  expire: 3600
  super: admin
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值