【Gin-Web】Bluebell社区项目梳理2:JWT-Token认证

news/2025/2/23 0:54:49/

本文目录

  • 一、JWT
    • Header
    • Payload
    • Signature
  • 二、代码解析
    • 刷新Token
    • 鉴权中间件

JWT_3">一、JWT

JWT是Json Web Token的缩写。JWT本身是没有定义任何技术实现,只是定义了一种基于Token的会话管理规则,涵盖Token需要包含的标准内容和Token生成过程,特别适用于分布式站点的单点登录SSO场景。

在这里插入图片描述

在这里插入图片描述
JWT长这样,三个部分分别是头部、负载、签名。

头部和负载以Json的形式存在,就是JWT中的Json,三部分的内容分别经过了Base64编码,然后拼接成一个JWT token

Header

头部存储了所使用的加密算法还有Token类型。比如
{ "alg":"HS2546","TYP":"jwt" }.

Payload

将Token当成是一个载体,表示Token里面装了什么,也是一个json对象。JWT规定了7个官方字段给开发者使用。

在这里插入图片描述
除了官方字段,开发者也可以指定字段和内容,比如下面的。
在这里插入图片描述
JWT默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个不分,这个JSON对象也要使用Base64URL算法转成字符串。

Signature

Signature是对前两个部分的签名,防止篡改。

首先需要指定一个秘钥,这个密钥只有服务器才知道,不能泄露给用户。然后使用Header里面指定的签名算法(默认SHA 256),按照下面的公式产生签名。

token function">HMACSHA256token punctuation">(token function">base64UrlEncodetoken punctuation">(headertoken punctuation">)token operator">+token string">"."token operator">+token function">base64UrlEncodetoken punctuation">(payloadtoken punctuation">)token punctuation">,secrettoken punctuation">)

JWT拥有基于Token的会话管理方式所拥有的一切优势,不依赖Cookie,使得其可以防止CSRF攻击,也能在禁用Cookie的浏览器环境中正常运行。
1、jwt基于json,非常方便解析;
2、可以在令牌中自定义丰富的内容,易扩展;
3、通过非对称加密算法及数字签名技术。

JWT的最大优势是服务端不再需要存储Session,使得服务端认证鉴权业务可以方便扩展,避免存储Session所需要引入的Redis等组件,降低了系统架构复杂度。

但这也是JWT最大的劣势,由于有效期存储在Token 中,JWT Token一旦签发,就会在有效期内一直可用(所以一般设置的时间会比较短),无法在服务端废止,当用户进行登出操作,只能依赖客户端删除掉本地存储的Token,如果需要禁用用户,单纯使用JWT就无法做到了。

二、代码解析

在这里插入图片描述

token keyword">type MyClaims token keyword">struct token punctuation">{UserID   token builtin">uint64 token string">`json:"user_id"`Username token builtin">string token string">`json:"username"`jwttoken punctuation">.StandardClaims
token punctuation">}

定义一个自定义的 JWT 声明结构体 MyClaims,用于扩展标准的 JWT 声明以满足特定需求。jwt.StandardClaims 是 JWT 包自带的结构体,包含了官方定义的标准字段(如 Issuer、ExpiresAt 等)。由于标准声明中没有包含用户 ID 和用户名等自定义信息,因此通过内嵌 jwt.StandardClaims 并添加额外字段(如 UserID 和 Username),可以将这些自定义信息嵌入到 JWT 中。这样,生成的 JWT 令牌不仅包含标准的 JWT 信息,还能携带用户相关的自定义数据,方便在后续的认证和授权过程中使用。

token keyword">var mySecret token operator">= token punctuation">[token punctuation">]token function">bytetoken punctuation">(token string">"Golinie"token punctuation">)token keyword">func token function">keyFunctoken punctuation">(token boolean">_ token operator">*jwttoken punctuation">.Tokentoken punctuation">) token punctuation">(i token keyword">interfacetoken punctuation">{token punctuation">}token punctuation">, err token builtin">errortoken punctuation">) token punctuation">{token keyword">return mySecrettoken punctuation">, token boolean">nil
token punctuation">}

参数 _ *jwt.Token 是一个占位符,表示这个函数接收一个 JWT 令牌对象,但在这个实现中并没有使用它(因此用 _ 忽略)。

函数返回两个值:
i interface{}:返回密钥,这里返回的是全局变量 mySecret。
i interface{}:表示函数返回的第一个值是一个空接口类型。空接口可以存储任何类型的值,因此可以返回任何类型的数据。

err error:返回可能发生的错误。在这个实现中,没有错误发生,因此返回 nil。

token comment">// GenToken 生成access token 和 refresh token
token keyword">func token function">GenTokentoken punctuation">(userID token builtin">uint64token punctuation">, username token builtin">stringtoken punctuation">) token punctuation">(aTokentoken punctuation">, rToken token builtin">stringtoken punctuation">, err token builtin">errortoken punctuation">) token punctuation">{token comment">// 创建一个我们自己的声明c token operator">:= MyClaimstoken punctuation">{userIDtoken punctuation">,     token comment">// 自定义字段token string">"username"token punctuation">, token comment">// 自定义字段jwttoken punctuation">.StandardClaimstoken punctuation">{ token comment">// JWT规定的7个官方字段ExpiresAttoken punctuation">: timetoken punctuation">.token function">Nowtoken punctuation">(token punctuation">)token punctuation">.token function">Addtoken punctuation">(timetoken punctuation">.token function">Durationtoken punctuation">(vipertoken punctuation">.token function">GetInttoken punctuation">(token string">"auth.jwt_expire"token punctuation">)token punctuation">) token operator">* timetoken punctuation">.Hourtoken punctuation">)token punctuation">.token function">Unixtoken punctuation">(token punctuation">)token punctuation">, token comment">// 过期时间Issuertoken punctuation">: token string">"bluebell"token punctuation">, token comment">// 签发人token punctuation">}token punctuation">,token punctuation">}token comment">// 加密并获得完整的编码后的字符串tokenaTokentoken punctuation">, err token operator">= jwttoken punctuation">.token function">NewWithClaimstoken punctuation">(jwttoken punctuation">.SigningMethodHS256token punctuation">, ctoken punctuation">)token punctuation">.token function">SignedStringtoken punctuation">(mySecrettoken punctuation">)token comment">// refresh token 不需要存任何自定义数据rTokentoken punctuation">, err token operator">= jwttoken punctuation">.token function">NewWithClaimstoken punctuation">(jwttoken punctuation">.SigningMethodHS256token punctuation">, jwttoken punctuation">.StandardClaimstoken punctuation">{ExpiresAttoken punctuation">: timetoken punctuation">.token function">Nowtoken punctuation">(token punctuation">)token punctuation">.token function">Addtoken punctuation">(timetoken punctuation">.Second token operator">* token number">30token punctuation">)token punctuation">.token function">Unixtoken punctuation">(token punctuation">)token punctuation">, token comment">// 过期时间Issuertoken punctuation">:    token string">"bluebell"token punctuation">,                              token comment">// 签发人token punctuation">}token punctuation">)token punctuation">.token function">SignedStringtoken punctuation">(mySecrettoken punctuation">)token comment">// 使用指定的secret签名并获得完整的编码后的字符串tokentoken keyword">return
token punctuation">}

代码首先定义了一个自定义的 JWT 声明结构体 MyClaims,它包含用户 ID 和用户名等自定义字段,以及 JWT 标准声明字段(如过期时间和签发人)。这些自定义字段允许在生成的 Access Token 中携带用户相关信息,方便后续的验证和授权。

通过 jwt.NewWithClaims 方法,结合自定义声明 MyClaimsHS256 签名方法,生成 Access TokenAccess Token 的过期时间通过配置文件动态获取(viper.GetInt(“auth.jwt_expire”)),并设置为从当前时间起的若干小时后过期。最终,使用全局密钥 mySecretAccess Token 进行签名并编码为字符串。

Refresh Token 的生成逻辑类似,但不需要携带自定义数据,仅使用 JWT 标准声明字段。用于在 Access Token 过期时刷新新的 Access Token。同样使用 mySecret 进行签名。

函数返回生成的 Access TokenRefresh Token,以及可能发生的错误。这两个令牌将被发送给客户端,客户端在后续请求中使用 Access Token 访问资源,当 Access Token 过期时,使用 Refresh Token 获取新的 Access Token


Access Token 是用户访问受保护资源的凭证,通常有效期较短(如几分钟到几小时)。这种设计减少了令牌被泄露后可能造成的损害,因为即使令牌被攻击者获取,它很快就会失效。用户在每次请求受保护资源时都需要携带 Access Token,因此它需要频繁地被验证和使用。

Refresh Token 的有效期通常较长(如几天到几周),用于在 Access Token 过期时刷新一个新的 Access Token。这样可以避免用户频繁重新登录,提升用户体验。Refresh Token 通常存储在客户端的更安全位置(如 HTTPOnly Cookie 或本地存储),并且不会直接用于访问受保护资源,因此即使被泄露,攻击者也无法直接利用它访问系统。

Access Token 和 Refresh Token 可以独立管理。例如,服务器可以在检测到异常行为时吊销 Refresh Token,而不会影响当前正在使用的 Access Token。

而 Refresh Token 虽然有效期长,但通常不会直接用于访问资源,且可以被服务器端更严格地管理(如限制使用次数、IP 绑定等)。

接下来看看解析Token和刷新Token的代码。

在这里插入图片描述

先看看解析Token:定义了一个名为 ParseToken 的函数,用于解析和验证 JWT 令牌。它接收一个字符串形式的 JWT 令牌 tokenString,并尝试将其解析为自定义的 MyClaims 声明结构体。函数通过调用 jwt.ParseWithClaims 方法,结合自定义的密钥函数 keyFunc 来解析令牌,并将解析后的声明存储在 claims 中。如果解析过程中出现错误,函数会返回错误;如果解析成功但令牌无效,函数会返回一个自定义错误 “invalid token”。

该函数的作用是确保传入的 JWT 令牌是有效的,并提取其中的声明信息(如用户 ID 和用户名)。通过这种方式,可以在后续的业务逻辑中安全地使用这些声明信息,例如验证用户身份或授权访问特定资源。


刷新Token

一图胜千言,看图说话。

在这里插入图片描述
所以说后端需要对外提供一个刷新Token的接口,前端需要实现一个当Access Toekn过期时自动请求刷新Token的接口获取新Access Token的拦截器。

在这里插入图片描述
首先尝试解析 Refresh Token。如果解析失败(例如 Refresh Token 无效或已过期),函数直接返回错误。

尝试解析 Access Token,并提取其中的声明信息(MyClaims)。如果解析失败,会捕获错误并尝试将其转换为 jwt.ValidationError,以便进一步处理。

如果 Access Token 的错误类型是 jwt.ValidationErrorExpired(即 Access Token 已过期),并且 Refresh Token 仍然有效(前面已经验证过),则调用 GenToken 函数生成新的 Access Token 和 Refresh Token。(返回一个token也可以,看情况自己定义)。

鉴权中间件

当涉及权限的某些接口,就需要Token验证了,比如说只有登录了之后才能进行评论。

也就是通过了鉴权,才能访问下面的接口。

为一组路由应用 JWT 认证中间件,定义多个受保护的 HTTP 接口。通过调用 middlewares.JWTAuthMiddleware(),所有定义在该中间件下的路由都将要求客户端提供有效的 JWT 令牌才能访问,从而确保这些接口的安全性。这些路由涵盖了创建帖子、投票、评论等操作,以及一些数据查询接口,例如获取帖子详情、评论列表等。
在这里插入图片描述

来看看基于JWT的认证中间件的实现。

在这里插入图片描述

这里我们可以使用post工具来模拟。
在这里插入图片描述

上面的代码首先从 HTTP 请求头的 Authorization 字段中获取令牌,检查令牌是否存在且格式是否正确(假设以“Bearer”开头)。如果令牌缺失或格式错误,中间件会返回错误响应并终止请求处理。如果令牌格式正确,它会调用 jwt.ParseToken 函数解析 JWT 令牌,并验证其有效性。如果解析失败,中间件会返回错误并终止请求;如果解析成功,它会将解析出的用户 ID 存储到 Gin 的上下文 c 中,供后续的处理函数使用,然后继续执行后续的请求处理流程。

这里对用户的ID进行了封装,也就是获取当前用户的ID,方便后续的操作。

getCurrentUserID 函数通过从上下文中获取用户 ID,并进行类型断言,确保了获取到的用户 ID 是有效的。如果用户未登录或上下文中没有用户 ID,函数会返回一个明确的错误 ErrorUserNotLogin,这有助于在业务逻辑中快速识别和处理未登录的情况。

在这里插入图片描述


http://www.ppmy.cn/news/1574284.html

相关文章

Rust 中的 `Drop` 特性:自动化资源清理的魔法

一、自动清理机制:Rust 的析构函数 在许多语言中,当程序结束或对象不再需要时,开发者必须显式调用清理函数来释放内存或关闭资源。Rust 则不然——它通过 Drop 特性实现了类似析构函数(destructor)的自动化清理机制。…

毕业项目推荐:基于yolov8/yolo11的100种中药材检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示:功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出(xls格式)功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

缺少网络安全组织架构 网络安全缺陷

🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 目录 TCP/IP概述 一、计算机网络的体系结构 1、计算机网络体系结构定义 2、OSI/RM与TCP/IP对应关系 3、名词释义 4、TCP/IP协议主要构成和功能 5、各层次间…

视频HDR技术详解,你的电脑怎么播放HDR视频?

闲聊:前两天在b站上面看到影视飓风的视频,让我有点疑惑,我不知道为什么播放视频有设备撑不住一说,所以感兴趣去ytb下载了4k原片30hz刷新的,然后测试一下我的电脑能不能播放,发现还是可以的,视觉…

【Rust中级教程】1.16. 泛型trait:泛型(类型参数)trait、关联类型trait

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 这篇文章以概念性的和建议性的文字偏多,需要你对泛型类型参数和关联类型有一定了…

【数据库系统概论】第第12章 并发控制

12.1 并发控制概述 并发控制是指数据库管理系统(DBMS)通过控制多个事务同时执行,保证数据的一致性和隔离性,避免事务间的相互干扰。 事务串行执行不能充分利用系统资源 并发执行的优点:能够减少处理机的空闲 时间&a…

【Gee】7天用 Go 从零实现 Web 框架 Gee

7天用 Go 从零实现 Web 框架 Gee 设计一个框架 在设计一个框架之前,我们需要回答框架核心为我们解决了什么问题。只有理解了这一点,才能想明白我们需要在框架当中实现什么功能。 首先我们来看一下标准库 net/http 如何处理一个请求: func…

计算机视觉(CV)

计算机视觉(CV)技术是人工智能(AI)领域的一个重要分支,其优势和挑战如下所示: 优势: 自动化处理:CV 技术可以自动处理大量图像和视频数据,实现高效的信息提取和分析&am…