在gin框架中使用JWT

什么是JWT?

JWT是一种跨域认证解决方案,规定了一种Token实现方式,目前多用于前后端分离项目

为什么需要JWT?

在之前的一些web项目中,我们通常使用的是Cookie-Session模式实现用户认证。相关流程大致如下:

  1. 用户在浏览器端填写用户名和密码,并发送给服务端
  2. 服务端对用户名和密码校验通过后会生成一份保存当前用户相关信息的session数据和一个与之对应的标识(通常称为session_id)
  3. 服务端返回响应时将上一步的session_id写入用户浏览器的Cookie
  4. 后续用户来自该浏览器的每次请求都会自动携带包含session_id的Cookie
  5. 服务端通过请求中的session_id就能找到之前保存的该用户那份session数据,从而获取该用户的相关信息。

这种方案依赖于客户端(浏览器)保存Cookie,并且需要在服务端存储用户的session数据。

web应用可能是前后端分开部署在不同的端口,有时候我们还需要支持第三方登录,这下Cookie-Session的模式就有些力不从心了

JWT就是一种基于Token的轻量级认证模式,服务端认证通过后,会生成一个JSON对象,经过签名后得到一个Token(令牌)再发回给用户,用户后续请求只需要带上这个Token,服务端解密之后就能获取该用户的相关信息了

生成JWT和解析JWT

我们在这里直接使用jwt-go这个库来实现我们生成JWT和解析JWT的功能

1.定义需求

jwt包自带的jwt.StandardClaims只包含了官方字段,我们需要根据自己的需求来决定JWT中保存哪些数据,比如我们规定在JWT中要存储username信息,那么我们就定义一个Claims结构体如下:

type Claims struct {
	UserId uint
	jwt.StandardClaims
}

然后我们定义JWT的过期时间,以及jwt加密的密钥

var jwtKey = []byte("a_secret_crect")
const TokenExpireDuration = time.Hour * 2

2.生成JWT

根据定义的结构体,创建声明,指定token过期时间,发放时间,发放人

使用指定的签名方法创建签名对象

使用密钥签名并获得完整的编码后的字符串token

claims := &Claims{
		UserId: user.ID,
		StandardClaims: jwt.StandardClaims{
			//token过期时间
			ExpiresAt: expireTime.Unix(),
			//发放时间
			IssuedAt: time.Now().Unix(),
			//发放人
			Issuer:  "zhaoheng",
			Subject: "user token",
		},
	}
// 使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
//使用指定的secret签名并获得完整的编码后的字符串token
tokenString, err := token.SignedString(jwtKey)

3.解析JWT

使用jwt.ParseWithClaims函数,将字符串token传进去

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

在gin框架中使用JWT

1.发放token

登录控制器

//发放token
token, err := common.ReleaseToken(user)

2.基于JWT的认证中间件

func AuthMiddleWare() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		//假设Token放在Header的Authorization中,并使用Bearer开头
		tokenString := ctx.GetHeader("Authorization")
		if tokenString == "" || !strings.HasPrefix(tokenString, "Bearer ") {
			ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
			ctx.Abort()
			return
		}
		//去掉Bearer
		tokenString = tokenString[7:]
		//使用之前定义好的解析JWT的函数来解析它
		token, claims, err := common.ParseToken(tokenString)
		if err != nil || !token.Valid {
			ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
			ctx.Abort()
			return
		}
		//验证通过,获取claim中的userid
		userId := claims.UserId
		DB := common.GetDB()
		var user model.User
		DB.First(&user, userId)

		if user.ID == 0 {
			ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
			ctx.Abort()
			return
		}
		//如果用户存在,将user信息写入上下文
		//后续的处理函数可以用过c.Get("user")来获取当前请求的用户信息
		ctx.Set("user", user)
		ctx.Next()
	}
}

3.注册路由,发请求验证

r.GET("/api/auth/info", middleware.AuthMiddleWare(), controller.Info)
func Info(ctx *gin.Context) {
	user, _ := ctx.Get("user")
	ctx.JSON(200, gin.H{"code": 200, "data": gin.H{"user": user}})

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值