首页 > 其他分享 >在GIn框架中使用JWT

在GIn框架中使用JWT

时间:2023-09-01 20:47:20浏览次数:38  
标签:return 框架 jwt JWT token Token gin GIn

在gin框架中使用JWT

 

JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下。

什么是JWT?

JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token 实现方式,目前多用于前后端分离项目和 OAuth2.0 业务场景下。

为什么需要JWT?

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

  1. 用户在浏览器端填写用户名和密码,并发送给服务端

  2. 服务端对用户名和密码校验通过后会生成一份保存当前用户相关信息的session数据和一个与之对应的标识(通常称为session_id)

  3. 服务端返回响应时将上一步的session_id写入用户浏览器的Cookie

  4. 后续用户来自该浏览器的每次请求都会自动携带包含session_id的Cookie

  5. 服务端通过请求中的session_id就能找到之前保存的该用户那份session数据,从而获取该用户的相关信息。

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

在移动互联网时代,我们的用户可能使用浏览器也可能使用APP来访问我们的服务,我们的web应用可能是前后端分开部署在不同的端口,有时候我们还需要支持第三方登录,这下Cookie-Session的模式就有些力不从心了。

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

想要了解JWT的原理,推荐阅读:阮一峰的JWT入门教程

安装

我们使用GO语言社区中的JWT相关库来构建我们的应用,例如:https://github.com/golang-jwt/jwt

 go get github.com/golang-jwt/jwt/v4

本文将使用这个库来实现我们生成JWT和解析JWT的功能

使用

默认Claim

如果我们直接使用JWT中默认的字段,没有其他定制化的需求则可以直接使用这个包中的和方法快速生成和解析token。

 package main
 ​
 import (
   "github.com/golang-jwt/jwt/v4"
   "time"
 )
 ​
 // 用于签名的字符串
 var mySigningKey = []byte("daihao.com")
 ​
 // GenRegisteredClaims 使用默认声明创建jwt
 func GenRegisteredClims() (string, error) {
   // 创建claims
   claims := &jwt.RegisteredClaims{
     ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), // 过期时间
     Issuer:    "June",
   }
   // 生产token对象
   token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
   // 生成签名字符串
   return token.SignedString(mySigningKey)
 }
 ​
 // ParseRegisteredClaims 解析jwt
 func ValidateRegisteredClaims(tokenString string) bool {
   // 解析token
   token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
     return mySigningKey, nil
   })
   if err != nil {
     return false
   }
   return token.Valid
 }
 ​

自定义Caims

我们需要定制自己的需求来决定JWT保存哪些数据,比如我们规定在JWT中要存储,username信息,那么我们就定义一个MyClaims结构体如下:

 // CustomClaims 自定义声明类型 并内嵌jwt.RegisteredClaims
 // jwt包自带的jwt.RegisteredClaims只包含了官方字段
 // 假设我们这里需要额外记录一个username字段,所以要自定义结构体
 // 如果想要保存更多信息,都可以添加到这个结构体中
 type CustomClaims struct {
   // 可根据需要自行添加字段
   Username string `json:"username"`
   jwt.RegisteredClaims  // 内嵌标准的声明
 }

然后我们定义JWT的过期时间,这里以24小时为例:

 const ToeknExpireDuration = time.Hour * 24

接下来还需要定义一个用户签名的字符串:

 // CustomSecret 用于加严的字符串
 var CustomSecret = []byte("夏天夏天悄悄过去")

生产JWT

我们可以根据自己的业务需求封装成一个生产token的函数。

 // GenToken 生成JWT
 func GenToken(username string) (string, error) {
   // 创建一个我们自己的空间
   claims := CustomClaims{
     username, // 自定义字段
     jwt.RegisteredClaims{
       ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
       Issuer:    "my-project", // 签发人
     },
   }
   
   // 使用指定的签名方法创建签名对象
   token := jwt.NewWithClaims(jwt.SigningMethodES256,claims)
   // 使用指定的secret签名并获得完整的编码后字符串token
   return token.SignedString(CustomSecret)
 }

解析JWT

根据给定的JWT字符串,解析出数据。

 // ParseToken  解析token
 func ParseToken(tokenString string) (*CustomClaims, error) {
   // 解析token
   //  如果是自定义Claim结构体则需要使用 ParseWithClaims方法
   token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
     // 直接使用标准的Claim则可以直接使用Parse方法
     //token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {})
     return CustomSecret, nil
   })
   if err != nil {
     return nil, err
   }
   // 对token对象中的Claim进行类型断言
   if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { // 检验token
     return claims, err
   }
   return nil, errors.New("invalid token")
 }

在Gin框架中使用JWT

首先我们注册一条路由/auth,对外提供获取token的渠道:

 r.POST("/auth",authHandler)

我们的authHandler定义如下:

 func authHandler(c *gin.Context) {
   // 用户发送用户名和密码过来
   var user UserInfo
   err := c.ShouldBind(&user)
   if err != nil {
     c.JSON(200, gin.H{
       "code": 2001,
       "msg":  "无效的参数",
     })
     return
   }
   // 校验用户名和密码是否正确
   if user.Username == "June" && user.Password == "June123" {
     // 生成token
     tokenString, _ := GenToken(user.Username)
     c.JSON(200, gin.H{
       "code": 2000,
       "msg":  "success",
       "data": gin.H{"token": tokenString},
     })
     return
   }
   c.JSON(200, gin.H{
     "code": 2002,
     "msg":  "鉴权失败",
   })
   return
 }

用户通过上面的接管口获取Token之后,后续就会携带者Token再来请求我们的其他接口,这个时候就需要对这些请求的Token进行校验操作了,很显然我们应该实现一个校验Token的中间件,具体实现如下:

 // 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 == "" {
       c.JSON(200, gin.H{
         "code": 2003,
         "msg":  "请求头中auth为空",
       })
       c.Abort()
       return
     }
     // 按空格分隔
     parts := strings.SplitN(authHeader, " ", 2)
     if !(len(parts) == 2 && parts[0] == "Bearer") {
       c.JSON(200, gin.H{
         "code": 2004,
         "msg":  "请求头中auth格式有误",
       })
       c.Abort()
       return
     }
 ​
     // parts[1]是获取到TokenString,我们使用之前定义的解析JWT的函数来解析它
     mc, err := ParseToken(parts[1])
     if err != nil {
       c.JSON(200, gin.H{
         "code": 2005,
         "msg":  "无效的Token",
       })
       c.Abort()
       return
     }
     // 将当前请求的username信息保存到请求的上下文c上
     c.Set("username", mc.Username)
     c.Next() // 后续的出路函数可以用c.Get("username")来获取当前请求的用户信息
   }
 }

注册一个/home路由,发个请求验证一下吧。

 r.GET("/home", JWTAuthMiddleware(), homeHandler)
 ​
 func homeHandler(c *gin.Context) {
   username := c.MustGet("username").(string)
   c.JSON(200, gin.H{
     "code": 200,
     "msg":  "success",
     "data": gin.H{"username": username},
   })
 }

如果不想自己实现上述功能。你也可以使用GitHub上别人封装好的包。比如:https://github.com/appleboy/gin-jwt

refresh token

在某些业务场景下,我们可能还需要使用refresh token。

这里可以参考https://datatracker.ietf.org/doc/html/rfc6749#section-1.5

标签:return,框架,jwt,JWT,token,Token,gin,GIn
From: https://www.cnblogs.com/xiaohaoge/p/17672784.html

相关文章

  • MegEngine 7-8 双月报来啦:新版本发布,开发者福利课程,干货满满
    v1.13.1新版本发布MegCC新版本发布【MegEngine使用小技巧】系列文章1.《MegEngine使用小技巧:如何使用MegCC进行模型编译》2.《MegEngine使用小技巧:Profiler使用手册》Imperative介绍专栏1.《MegEnginePython层模块串讲(上)》2.《MegEnginePython层模块串讲(中)》3.《Meg......
  • C#/.NET/.NET Core优秀项目和框架8月简报
    前言公众号每月定期推广和分享的C#/.NET/.NETCore优秀项目和框架(公众号每周至少推荐两个优秀的项目和框架当然节假日除外),公众号推文有项目和框架的介绍、功能特点以及部分截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排......
  • AtCoder Beginner Contest 317 C - Remembering the Days
    C-RememberingtheDays原题链接题意:每个点最多经过一次,求最长路思路:数据范围很小,深搜每个点能到其他点的所有路,取最大#include<bits/stdc++.h>usingnamespacestd;constintN=110;intg[N][N];intn,m;boolst[N];intw=0;intans=0;voiddfs(intu){ st[......
  • nginx优化相关
    https://blog.csdn.net/liuxiao723846/article/details/46862381Nginx反向代理,当后端为Https时的一些细节和原理_nginx反向代理https_赶路人儿的博客-CSDN博客 nginx-寒星12345678999-博客园(cnblogs.com)......
  • AtCoder Beginner Contest 317
    A-Potions#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongintpower(intx,inty,intp){x%=p;intans=1;while(y){if(y&1)ans=ans*x%p;y>>=1,x=x*x%p;}......
  • Nginx 平滑升级
    目录一、playbooks概述1.playbook简介2.playbook使用场景3.playbooks的组成4.yaml基本语法规则5.yaml关键字6.yaml支持的数据结构(1)对象(Mapping)(2)数组(Sequence)(3)纯量(Scalar)二、playbook操作示例1.示例:安装Apache服务2.示例:定义、引用变量3.示例:指定远程主机sudo......
  • 学习aop以及nginx
    苍穹外卖第一天搭建前端后端环境先将代码提交到本地然后在git上创建仓库https://gitee.com/lh03062021/sky-take-out.git点击push定义远程仓库把复制好的粘贴上去就可以推送成功了。导入sql文件,编译父项目nginx反向代理,将前端发送的动态请求由nginx转发到后端服务器好......
  • AtCoder Beginner Contest 292 E - Transitivity
    E-Transitivity原题链接题意:对于一个有向图,进行加边操作,使最终任意的个点具有传递效果,即若a到b有边,b到c有边,那么a到c也要有边,问最少需要进行多少次操作,使得每个节点所能到达的地方都有直接的边,也就是最短距离为1思路:怎么加边才是最优的,举个例子a->b->c->d->e对于a点到其他......
  • AtCoder Beginner Contest 292 D - Unicyclic Components
    D-UnicyclicComponents原题链接题意:判断一个连通块的边和点个数是否相同思路:对它使用并查集吧点击查看代码#include<bits/stdc++.h>usingnamespacestd;constintN=200010,M=N<<1;//维护连通图中点和边的个数intsd[N],se[N],p[N];boolf[N];//谁是祖宗int......
  • nginx--添加stream模块
    使用的是openEuler22.03(LTS-SP2)系统,yum源选择清华大学的源清楚yum缓存后重新加载,nginx版本变成1.23.2[[email protected]]#yuminfonginxLastmetadataexpirationcheck:0:21:11agoonFri01Sep202310:29:45AMCST.InstalledPackagesName:......