Go-鉴权与JWT
基础知识
鉴权
计网和Gin基础知识:Link
我们都知道 HTTP 协议是无状态的,所谓的无状态就是客户端每次想要与服务端通信,都必须重新与服务端链接,意味着请求一次客户端和服务端就连接一次,下一次请求与上一次请求是没有关系的
这种无状态的方式就会存在一个问题:如何判断两次请求的是同一个人?就好比用户在页面 A 发起请求获取个人信息,然后在另一个页面同样发起请求获取个人信息,我们如何确定这俩个请求是同一个人发的呢?
为了解决这种问题,我们就迫切需要一种方式知道发起请求的客户端是谁?此时,cookie、token、session 就出现了,它们就可以解决客户端标识的问题,甚至是解决权限问题
cookie & session
通常情况下,cookie 和 session 都是结合着来用
因此,这里我们就将 cookie 和 session 结合着来用
cookie
概述
Cookie 是一种在客户端存储数据的技术,它是由服务器发送给客户端的小型文本文件,存储在客户端的浏览器中,大小限制大致在 4KB 左右
在客户端发送请求时,浏览器会自动将相应的 Cookie 信息发送给服务器,服务器通过读取 Cookie 信息,就可以判断该请求来自哪个客户端
因此,Cookie 可以用于存储用户的登录状态、购物车信息等
在以前很多开发人员通常用 cookie 来存储各种数据,后来随着更多浏览器存储方案的出现,cookie 存储数据这种方式逐渐被取代,主要原因有如下:
cookie 有存储大小限制,4KB 左右
字符编码为 Unicode,不支持直接存储中文
数据可以被轻易查看
流程图

session
概述
session 由服务端创建
当一个请求发送到服务端时,服务器会检索该请求里面有没有包含 sessionId 标识,如果包含了 sessionId,则代表服务端已经和客户端创建过 session,然后就通过这个 sessionId 去查找真正的 session,如果没找到,则为客户端创建一个新的 session,并生成一个新的 sessionId 与 session 对应,然后在响应的时候将 sessionId 给客户端,通常是存储在 cookie 中。如果在请求中找到了真正的 session,验证通过,正常处理该请求
每一个客户端与服务端连接,服务端都会为该客户端创建一个 session,并将 session 的唯一标识 sessionId 通过设置 Set-Cookie 头的方式响应给客户端,客户端将 sessionId 存到 cookie 中。
流程图

token
概述
Token 是一种在客户端和服务端之间传递身份信息的方式
当用户登录成功后,服务端会生成一个 Token,将其发送给客户端
客户端在后续的请求中,需要将 Token 携带在请求头或请求参数中
服务端通过验证 Token 的合法性,就可以确定该请求来自哪个用户,并且可以根据用户的权限进行相应的操作
组成
Token是一个由一串字符组成的令牌,用于在计算机系统中进行身份验证和授权
token通常由三个部分组成:标头、有效载荷、签名
- 标头(Header):包含了加密算法和类型,用于指定如何对有效载荷进行编码和签名。常用的算法有HMAC、RSA、SHA等
- 有效载荷(Payload):包含了一些信息,如用户ID、角色、权限等,用于验证身份和授权。有效载荷可以是加密的,也可以是明文的
- 签名(Signature):是对标头和有效载荷进行密钥签名后得到的值,用于验证token的完整性和真实性
一个完整的token包含了标头、有效载荷和签名三个部分,它们一起构成了一个安全的令牌,用于进行身份验证和授权。
认证流程(Access token)
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
- 客户端收到 token 以后,会把它存储起来,比如放在 localStorage 里
- 客户端每次发起请求的时候需要把 token 放到请求的 Header 里传给服务端
- 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

Refresh Token
另外一种 token——refresh token
refresh token 是专用于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作
Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了
Refresh Token 及过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求
JWT
概述
JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案,是一种认证授权机制,是token的一种实现形式
JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准。、
JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源
比如用在用户登录上。 可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名
因为数字签名的存在,这些传递的信息是可信的。
流程图


组成
JWT 生成示例:
1
2
3
4
5
6
7
8
9
10
11
12Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"user_id": 114514,
"role": "admin",
"exp": 1716579600
}

生成的 JWT:
1
2
3
4
5eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxNjU3OTYwMH0
.
abc123signature客户端:
- 接收到服务器返回的 JWT,将其存储在客户端(通常是
localStorage或sessionStorage也可以是cookie) - 后续请求中,客户端会将 JWT 作为凭证发送给服务器
- 在请求受保护资源时,客户端通过 HTTP 请求头将 JWT 发送到服务器
- 请求头中通常使用
Authorization: Bearer <token>格式
- 接收到服务器返回的 JWT,将其存储在客户端(通常是
服务端:
1.检查请求头中的
Authorization是否包含 JWT。Authorization: Bearer <具体的token字符串>
2.提取 JWT 并验证:
- **签名验证:**使用密钥验证签名是否正确,确保 Token 未被篡改。
- **过期时间验证:**检查
exp字段是否未过期。 - **其他验证:**检查
iss(发行者)和aud(受众)是否匹配。
验证成功后,解析
Payload,提取用户信息和权限。如果 Token 无效或过期,返回 401 Unauthorized 错误
因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要
因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制生成
JWT代码实现
依赖:
- gin框架:
go get -u github.com/gin-gonic/gin - JWT:
go get -u github.com/golang-jwt/jwt/v4 - time包(存储Expiration过期时间)
- strings包(分解请求头信息)
- errors包(返回错误信息)
基础知识
声明(Claims)
JWT声明默认信息如下:
| 声明简称 | 英文全称 | 含义与作用 |
|---|---|---|
iss |
Issuer | JWT的签发者,标识创建并签发该令牌的实体(例如某个认证服务器或服务)。 |
sub |
Subject | JWT的主题,标识该令牌所指向的主体(通常是用户ID),即令牌是关于谁的。 |
aud |
Audience | JWT的受众,指定该令牌意图提供给哪个或哪些接收方使用,接收方需验证自身是否在受众列表中。 |
exp |
Expiration Time | JWT的过期时间,一个Unix时间戳,超过此时间后令牌应被视为无效。 |
nbf |
Not Before | JWT的生效时间,一个Unix时间戳,在此时间之前令牌应被视为无效。 |
iat |
Issued At | JWT的签发时间,标识令牌是何时被创建的。 |
jti |
JWT ID | JWT的唯一标识符,为令牌提供一个唯一标识,常用于防止同一令牌被重复使用(重放攻击)。 |
type token:
在jwt中,jwt.token是一种数据类型,用于储存token信息,详细数据如下:
1 | type Token struct { |
实现流程
1. 设置JWT默认配置
在config包下:
1 | package config |
以上代码设置了JWT的默认配置DefaultJWTConfig,用于后续JWT配置的使用
2. 设置JWT具体方法
在util包下:
1 | package utils |
以上代码创建了三个JWT方法:生成 tokenString、验证 JWT Token和提取 JWT 声明信息,注意这三个方法的参数以及返回值
3. 创建JWT认证中间件
1 | package middleware |
以上代码创建了一个JWT认证中间件JWTAuthMiddleware,路由注册该中间件后,对该路由的请求会流过该中间件,验证请求头中是否含有token以及token是否合法
4. 为受保护的路由注册中间件
1 | package routes |
现在访问http://localhost:8080/api/info 会验证JWT
实现效果
Demo:Github
Article:My-Blog
使用Postman测试api后:
