type
status
date
slug
summary
tags
category
icon
password
📝 主旨内容
Token 生成方式
Token 的生成方式通常有以下几种:
- 随机字符串:可以使用一些随机数生成算法,如 UUID、Snowflake 等来生成一个随机的字符串作为 Token。由于随机字符串本身就是随机分布的,因此具有很高的安全性。
- JWT(JSON Web Token):JWT 是一种基于 JSON 格式的开放标准(RFC 7519),用于在多方之间安全地传输信息。它将用户身份信息和权限等相关信息编码成一个 JSON 对象,并通过数字签名或者加密等方式进行验证和保护。JWT 除了可以用于 Token 登录外,还可以用于 API 认证、单点登录等场景。
- SessionID。
通常的Token在服务器端的实现方式有这几个:
- 用SessionID实现Token的功能
- 使用Json Web Token (JWT)
- 中心化存储Token
下面分析一下各个存储方式的优缺点。
Cookie + Session 登录
大家都知道,HTTP 是一种无状态的协议。
无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求返回数据,但不会记录任何信息。
为了解决 HTTP 无状态的问题,出现了 Cookie。
Cookie 是服务器端发送给客户端的一段特殊信息,这些信息以文本的方式存放在客户端,客户端每次向服务器端发送请求时都会带上这些特殊信息。
- 前端输入账号密码,提交给后端
- 后端验证成功后,创建一个
Session
。Session
是一种服务器端保存用户会话信息的机制,用于识别多次请求之间的逻辑关系。
- 后端将
Session ID
(通常是一个随机的字符串)返回给前端,并通过Cookie
的方式将Session ID
保存在浏览器中。这样就可以保证当用户再次发送请求时,后端可以通过该Session ID
来识别用户身份,并完成相关的操作。
- 在后续的请求中,浏览器会自动将保存的
Cookie
信息发送到后端进行验证,如果Session ID
有效,则返回相应的数据。如果Session ID
失效或者不存在,则需要重新登录获取新的Session ID
。
- 用户退出时,后端要删除对应的
Session
信息
cookie的设置原理
cookie的简单之处,在于前端是无感知的,无需额外开发。这是http协议的约定,后端可以通过返回的报文,将cookie设置进网页,网页下次请求也会自动携带。
SetCookie
命令缺点
- 跨域问题:由于 Cookie 只能在同域名下共享,因此跨域访问时无法访问到对应的 Cookie 信息。这时,可能需要采用一些其他的跨域解决方案,如 JSONP、CORS 等。
- 扩展性问题:由于 Session 信息存储在服务器端,当系统扩展到多台服务器时,需要采用一些集中式的 Session 管理方案,否则会出现 Session 不一致或者丢失等问题。
- 一些移动设备和浏览器可能会禁用 Cookie 和 Session 机制,这会导致无法正常登录
总结
- 给服务器的
sessionID
其实就相当于是一个token
,只不过前端是无感知的设置进cookie
的,这种方案 通常适用于后台。
- 由于cookie的一些限制,这个token最好还是由前端主动保存比如保存到localStorage。登录的时候主动从请求头携带。
- 由于现在都是集群部署,token的关系保存,最好又是集中化管理,或者无状态化管理
JWT实现Token
简单来说JWT就是通过可逆加密算法,生成一串包含用户、过期时间等关键信息的Token,每次请求服务器拿到这个Token解密出来就能得到用户信息,从而判断用户状态。
优点
- JWT的最大特点是服务器不保存会话状态,无论哪个服务器解析出来的Token信息都一样,而且不需要做任何查询操作,省掉了数据库/Redis的开销
缺点
- 正式因为JWT的特点,使用期间不可能取消令牌或更改令牌的权限,一旦JWT签发,在有效期内将会一直有效。
- 无法主动更新Token的有效性,只要用户传回来的Token没有过期,服务器就会认为这个用户操作是有效的。比如一下这个场景:某用户被封禁,此时该用户所有操作都应该被禁止,但是由于之前发给用户的JWT Token还没有过期,服务器仍然认为该用户操作合法。有一个解决方案是维护一张JWT黑名单表,只有没在表上的用户的JWT是有效的。但是随之而来又有一个问题便是这个JWT黑名单表存在哪里。存在服务器,那么又要搞多服务器同步。存在关系数据库,那么查数据库效率又低。存在Redis,则又回到了Token丢失问题。
- 其实解析JWT Token也是消耗服务器CPU的
总结
- 由于jwt是无状态的,它一发布开始,就意味着固定了过期时间。我们没法对他做失效,没法实现续期,它的好处也是显而易见的。不需要任何一个中心化的地方去保存它,管理它,查询它,比对它。
双token方案
双token是为了解决jwt的续期问题的。由于jwt一颁布,就意味着在指定时间内能够通行。
- 如果给的有效期过长,风险是比较大的,服务器失去了掌控力。在这期间如果想让用户失效,或者是有人盗取了token。都可以胡作非为好久。
- 如果给的有效期过短,用户经常需要重新登录,体验也很不好。
- 如果中心化管理用户状态,也就是每次解析jwt token之后,还需要去中心化比对能否通过。这样又违背了初衷。增加每次认证的耗时
双token分为
access_token
和refresh_token
。一般access_token
的有效期可以设置为10分钟,refresh_token
的有效期可以设置为7天。用户每次请求都用access_token
,如果前端发现请求401,也就是过期了,就用refresh_token
去重新申请一个access_token
。继续请求。这里的关键在于,
refresh_token
申请access_token
的时候,用户是无感知的,前后端的框架自动去更新这个新的access_token
。还有一个点在申请
access_token
的时候,后端这时候会去校验用户的状态等问题,如果发现用户被禁用了,就申请不到token了。总结
双token是一个多方平衡的完美方案。它希望对用户的认证有所掌控,又不希望每次的检验会增加耗时。它不想给用户过长的授权时间,又不想用户因此频繁登录影响体验。因此变成了每隔一段
access_token
的过期时间,都会重新掌控局面,进行重新认证的复杂判断。中心管理token
JWT碰巧有去中心化的特性,但为了能够控制它的上下线,主动下线 ,登录续期等功能。我们依然可以对它进行中心化的管理。
这也是抹茶当前采用的方式。依赖redis中心化管理uid-》token的信息。确保一个uid只有一个有效的token。用户登录后,每一次认证都会解析出uid,并请求redis进行token比对。并且异步判断有效期小于一天,进行续期。
有人说为啥不用uuid做token呢,既然都是redis中心存储,用uuid还可以少一次解析。
如果用uuid,前端每一次请求除了带上uuid还需要带上uid。
因为单纯用uuid,黑客很有可能不断遍历uuid去撞库,碰巧撞到有关联的在线用户。而如果将uuid和uid一起比对,哪怕uuid碰巧撞到了登录的用户,还需要确保是相同的uid。这个概率会降低非常之多。
用jwt的话,正好包含了uid,让前端传起来方便,所以就这么选择了。
大家明白了其中的差别,到时候就懂得怎么去对线面试官的。其实我们用jwt,但是却没怎么用到它的特性。本质上这样的场景用个uuid就差不多了
代码实现
底层登录需要开几个接口,续期,解析,生成token
jwt的工具类
引入依赖
配置文件
🤗 总结归纳
总结文章的内容
📎 参考文章
- 一些引用
- 引用文章
有关Notion安装或者使用上的问题,欢迎您在底部评论区留言,一起交流~
- 作者:三-岁
- 链接:https://mozhiyuan.top/article/db1648bd-d35a-4faf-a1bb-6b30da133811
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。