UUID+token+JWT
UUID
UUID(Universally Unique Identifier,通用唯一识别码)
UUID是由一组32位的16进制数字所构成
格式:8-4-4-4-12
Java中使用UUID
1 | String uuid = UUID.randomUUID().toString().replaceAll( "-" , "" ); |
token
token主要有两个作用
- 防止表单重复提交
- 身份验证
防止表单重复提交
防止表单重复提交一般采用前后端都限制的方式。比如:前端点击提交之后,将按钮禁用,不可再次点击;客户端和服务端的token各自独立存储,客户端存储在Cookie或者Form的隐藏域中,服务端存储在Session或者其他缓存系统中(redis)
实现思路:客户端刚刚进入页面的时候调用后端代码,后端生成一个token并存储在Session中,然后将token返回给客户端,客户端存储在Cookie中,点击提交比较前后端的token。token一致则移除Session中的token,提交成功;若token不一致,则提交失败
身份验证
步骤
- 客户端请求登录
- 服务端验证登录
- 验证成功,服务端生成一个token存储在Session中,并返回给客户端
- 客户端保存token(存储在Cookie或Local Storage中)
- 客户端每次向服务端请求资源时都带着token
- 服务端收到请求,验证token,成功则向客户端返回请求的数据
传统的session认证
http协议本身是一种无状态的协议,这意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为只根据http协议,我们并不能知道是哪个用户发送过来的请求。所以为了让我们的应用能够识别是哪个用户发送过来的请求,我们可以在服务器存储一份用户登录的信息,然后将其响应给浏览器,浏览器保存在cookie中,下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的session认证
session认证所存在的问题
- 每个用户经过我们的的应用验证后,都产生一个session,通常session都是保存在内存中,随着认证用户的增多,服务端的开销明显增大
- 若session被保存在内存中,这意味着用户下次请求时必须还在这台服务器上进行请求,这样在分布式应用上限制了负载均衡器的能力,限制了应用的扩展能力
- 因为是基于cookie进行用户识别,cookie如果被截获,用户容易收到跨站请求伪造的攻击
基于token的鉴权机制
基于token的鉴权机制类似于http协议也是无状态的,它不需要服务端保留用户的认证信息或者会话信息。这意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了
Session的状态是存储在服务器端,客户端只有session_id;而Token的状态是存储在客户端
JWT(Json Web Token)
JWT由三段信息构成,头部(header)+载荷(payload)+签名(signature)
header
JWT的头部承载两部分信息
- 声明类型
- 声明的加密算法
1 2 3 4 | { 'typ' : 'JWT' , 'alg' : 'HS256' } |
然后将头部进行base64编码,构成了第一部分
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 |
payload
载荷就是存放有效信息的地方,这些有效信息包含三个部分
- registered claims
- public claims
- private claims
registered claims
iss:jwt签发者
sub:jwt所面向的用户
aud:接收jwt 的一方
exp:jwt的过期时间,这个过期时间必须大于签发时间
nbf:定义在什么时间之前,该jwt都是不可用的
iat:jwt的签发时间
jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
public claims
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的信息。但不建议添加敏感信息,因为Base64是对称解密的,意味着该部分信息可以归类为明文信息
private claims
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息
1 2 3 4 5 | { 'sub' : '123456' , 'name' : tang, 'admin' : true } |
然后将载荷进行Base64编码,得到第二部分
1 | eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 |
signature
为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然后对它们签名即可。
签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。
这个部分需要Base64编码后的header和payload使用 . 连接组成的字符串,然后通过header中声明的加密方式进行加密,然后就构成了第三部分
1 2 | HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) //TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ |
最终的JWT
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ |
注意:secret是保存在服务器端的,jwt的签发生成也是在服务端,secret就是用来进行jwt的签发和jwt的验证,所以它就是服务端的私钥
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术