前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >gin博客项目复盘--05 JWT全面解读、详细使用步骤

gin博客项目复盘--05 JWT全面解读、详细使用步骤

作者头像
微客鸟窝
发布2022-11-07 14:50:13
4240
发布2022-11-07 14:50:13
举报
文章被收录于专栏:Go语言指北Go语言指北

JWT

❝通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,可以用于在各方之间安全地将信息作为Json对象传输。 ❞

JWT 介绍

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串。

代码语言:javascript
复制
JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
  1. Header

JWT头是一个描述JWT元数据的JSON对象,alg 属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ 属性表示令牌的类型,JWT令牌统一写为JWT。

代码语言:javascript
复制
{
  "alg": "HS256",
  "typ": "JWT"
}
  1. Payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。JWT指定七个默认字段供选择

代码语言:javascript
复制
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:

代码语言:javascript
复制
{
  "sub": "1234567890",
  "name": "Helen",
  "admin": true
}
  1. Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的 header 和 payload 数据,通过指定的算法生成哈希,以确保数据不会被篡改。

首先,需要指定一个密钥(secret)。该密钥仅保存在服务器中,并且不能向用户公开。然后,使用 header 中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名

代码语言:javascript
复制
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
  • header 和 payload 可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据
  • signature 使用了不可逆的加密算法,无法解码出原文,它的作用是校验 token 有没有被篡改。

JWT 使用

参考资料:https://pkg.go.dev/github.com/dgrijalva/jwt-go/v4

下载:go get -u github.com/dgrijalva/jwt-go

JWT中间件:middleware/jwt.go

代码语言:javascript
复制
package middleware

import (
 "errors"
 "ginVue3blog/utils"
 "ginVue3blog/utils/errmsg"
 "github.com/dgrijalva/jwt-go"
 "github.com/gin-gonic/gin"
 "net/http"
 "strings"
)

type JWT struct {
 JwtKey []byte
}

func NewJWT() *JWT {
 return &JWT{
  []byte(utils.JwtKey),
 }
}

type MyClaims struct {
 Username string `json:"username"`
 jwt.StandardClaims
}

// 定义错误
var (
 TokenExpired     error = errors.New("Token 已过期,请重新登录")
 TokenNotValidYet error = errors.New("Token 无效,请重新登录")
 TokenMalformed   error = errors.New("Token 不正确,请重新登录")
 TokenInvalid     error = errors.New("这不是一个 Token,请重新登录")
)

// CreateToken 生成token
func (j *JWT) CreateToken(claims MyClaims) (string, error) {
 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
 return token.SignedString(j.JwtKey)
}

// ParserToken 解析token
func (j *JWT) ParserToken(tokenString string) (*MyClaims, error) {
 token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
  return j.JwtKey, 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 {
    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.(*MyClaims); ok && token.Valid {
   return claims, nil
  }
  return nil, TokenInvalid
 }
 return nil, TokenInvalid
}

// JwtToken jwt中间件
func JwtToken() gin.HandlerFunc {
 return func(c *gin.Context) {
  var code int
  tokenHeader := c.Request.Header.Get("Authorization")
  if tokenHeader == "" {
   code = errmsg.ERROR_TOKEN_EXIST
   c.JSON(http.StatusOK, gin.H{
    "status":  code,
    "message": errmsg.GetErrMsg(code),
   })
   c.Abort()
   return
  }

  checkToken := strings.Split(tokenHeader, " ")
  if len(checkToken) == 0 {
   c.JSON(http.StatusOK, gin.H{
    "status":  code,
    "message": errmsg.GetErrMsg(code),
   })
   c.Abort()
   return
  }

  if len(checkToken) != 2 || checkToken[0] != "Bearer" {
   c.JSON(http.StatusOK, gin.H{
    "status":  code,
    "message": errmsg.GetErrMsg(code),
   })
   c.Abort()
   return
  }

  j := NewJWT()
  //解析token
  claims, err := j.ParserToken(checkToken[1])
  if err != nil {
   if err == TokenExpired {
    c.JSON(http.StatusOK, gin.H{
     "status":  errmsg.ERROR,
     "message": "token授权已过期,请重新登录",
     "data":    nil,
    })
    c.Abort()
    return
   }
   //其他错误
   c.JSON(http.StatusOK, gin.H{
    "status":  errmsg.ERROR,
    "message": err.Error(),
    "data":    nil,
   })
   c.Abort()
   return
  }

  c.Set("username", claims)
  c.Next()
 }

}

登录 api/v1/login.go

代码语言:javascript
复制
package v1

import (
 "ginVue3blog/middleware"
 "ginVue3blog/model"
 "ginVue3blog/utils/errmsg"
 "github.com/dgrijalva/jwt-go"
 "github.com/gin-gonic/gin"
 "net/http"
 "time"
)

// Login 后台登
func Login(c *gin.Context) {
 var formData model.User
 _ = c.ShouldBindJSON(&formData)
 var token string
 var code int

 formData, code = model.CheckLogin(formData.Username, formData.Password)

 if code == errmsg.SUCCSE {
  setToken(c, formData)
 } else {
  c.JSON(http.StatusOK, gin.H{
   "status":  code,
   "data":    formData.Username,
   "id":      formData.ID,
   "message": errmsg.GetErrMsg(code),
   "token":   token,
  })
 }
}

// token生成函数
func setToken(c *gin.Context, user model.User) {
 j := middleware.NewJWT()
 claims := middleware.MyClaims{
  Username: user.Username,
  StandardClaims: jwt.StandardClaims{
   NotBefore: time.Now().Unix() - 100,
   ExpiresAt: time.Now().Unix() + 604800,
   Issuer:    "GinBlog",
  },
 }

 token, err := j.CreateToken(claims)

 if err != nil {
  c.JSON(http.StatusOK, gin.H{
   "status":  errmsg.ERROR,
   "message": errmsg.GetErrMsg(errmsg.ERROR),
   "token":   token,
  })
 }

 c.JSON(http.StatusOK, gin.H{
  "status":  200,
  "data":    user.Username,
  "id":      user.ID,
  "message": errmsg.GetErrMsg(200),
  "token":   token,
 })
 return
}

路由里使用

图片及部分相关技术知识点来源于网络搜索,侵权删!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微客鸟窝 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JWT
    • JWT 介绍
      • JWT 使用
      相关产品与服务
      消息队列 TDMQ
      消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档