JWT 的基本就不再过多阐述
思考的问题前提: 比如我们面对一个千万级用户的系统. * 客户端需要和服务端交互, 假设 50% 的接口需要校验 * 10% 的接口需要获取个人信息
使用网站: http://www.redis.cn/redis_memory/
1000w key 占用量
key 为 32 位字符串, value 如下(我按 128 个字节算):
{
"id": 999999,
"nickname": "seth",
"vip_exp": 1687153583,
"avatar": "https://wwww.baidu.com/a.png"
}
网站得出的结果是: 2.4G,
1
seth19960929 OP 还有 access_token 一个就够了, 为什么还要有 refresh_token, 除了官方说的安全之外, 就是可以作为两个时间点去使用.
access_token 每次都要校验, 可以简单的校验, refresh_token 使用的频次比较少, 这时候可以做更完整的校验. |
2
LeegoYih 2023-06-19 12:42:44 +08:00
用了 JWT 还要存 token 到服务端,每次还要查版本号和黑名单,不是脱裤子放屁吗?
用户权限之类的更新了怎么办?难道让用户退出重新登录一遍吗? 查一次缓存就能解决的事情,不要太小看 Redis 的性能了。 而且 JWT 不是 100%安全的: https://github.com/yihleego/jwtcrack |
3
oldshensheep 2023-06-19 12:53:10 +08:00
你这个都依赖 **分钟级别过期 access_token**
你加个 token_version 那每次刷新 jwt 就要查询数据库,而且你刷新的非常频繁,这不又和原来一样 同样的还有检查 device_id 要查询数据库 |
4
giter 2023-06-19 12:56:45 +08:00
我选择把 accessToken 与 refreshToken 存在 localStorage 中。既然要 JWT ,那就贯彻到底,给服务端彻底减负。
|
5
nomagick 2023-06-19 13:03:20 +08:00
jwt 真正的用途在于验证其他站点的用户和 token ,而不是自己站点的。
不要为了用而用。 |
6
ztxcccc 2023-06-19 13:08:16 +08:00
别查库了谢谢
|
7
amlee 2023-06-19 13:13:02 +08:00
jwt 的设计初衷就是要无状态,如果你的需求必须要求有状态,那么 jwt 的设计不适合你,为什么不用现成的 session 方案?
这本质上是一个需求和设计是否匹配的问题,搞清问题本质,跟技术优劣,或者说能否给技术打补丁以满足需求没关系。 板砖和锤子都能敲钉子,你非要说我能打磨一个完美的板砖去敲钉子,那我只能说脑子有包 |
9
CodeCodeStudy 2023-06-19 13:21:13 +08:00
token 存 redis 不就完事了?
|
10
emric 2023-06-19 13:21:29 +08:00
我这边用 last_login ,日期不相同就拒绝。
感觉你说的大部分问题,都是修改以下 last_login 就行。 |
11
seth19960929 OP @LeegoYih 我就问一下, 你使用 token 权限更新了怎么办? 这个和 jwt 没关系. 你权限更新其它方式该怎么做就怎么做. 不是我小看, 而是如果还不消耗存储, 不消耗 io 的方式为什么不讨论呢?
@oldshensheep token 的方式, 按用户级别来说, 每次请求来都查 redis, 并且 jwt 包含千万级别的用户数据, 而刷新的时候查询, 减少了 90% 的请求, 并且 redis 的存储只用非常少 @giter 没错了, 那肯定都要存客户端的, 和我说的没区别 @nomagick 你在哪看的定义? 你说的是 oauth2 吧 @ztxcccc 举个栗子 @amlee 又来一个不看解决方案, 上来就说不查库的 |
12
seth19960929 OP |
13
ellermister 2023-06-19 13:33:45 +08:00 via Android
那每个 jwt 请求到服务端不都是要查一下有没有在 redis 黑名单列表里,千万级的用户也是千万级的查询啊,jwt 只是把 redis 存储大小改了,没改变查询次数。
@seth19960929 |
14
seth19960929 OP @LeegoYih 我看了两眼你这个代码, 上来来个碰撞代码就说不安全, 能拿出具体的文献出来吗.
按你这样子说, 世界上没有安全的加密, 我只要写一个碰撞代码, 它们安全只是我现在没有碰撞到, 不代表我做不到. |
15
seth19960929 OP @ellermister 嗯嗯, 这是一个讨论帖子.
两种方案: 1. 只在刷新 token 的时候才查询是否在黑名单, 请求可以降低非常多, 有个问题就是会有延迟, 比如 5 分钟刷新一次 token, 那么就会加入后有 5 分钟延迟. 如果是部分场景完全可以用这种(只要 app 左右逻辑处理, 能及时失效, 如果有人窃取了 token, 那么它也只能使用 5 分钟, 到期了刷新就会得知被加入黑名单) 2. 大小还是差别很多的. 往 redis 里存 32 位字符串 key 1000w 个看看. 我等会给一个答案 |
17
8355 2023-06-19 14:00:50 +08:00
只用 jwt 没有防御措施绝对是错误的行为
单 ip 连续请求错误 token 值为不可信行为,这是风控策略应该做的事 在敏感操作必要时进行密码检查 仅传入 token 就给予最大用户操作权限本身就不合理 |
18
wunonglin 2023-06-19 14:01:36 +08:00
你说得对
|
19
Masoud2023 2023-06-19 14:04:19 +08:00
你文中的黑名单和单台设备下线,归根结底都是要去查库,那用 jwt 和不用 jwt 做 sessionid 有什么区别么
|
20
seth19960929 OP @LeegoYih
只要循环结束, 我一样能破解呀. for ($i = 1; $i < pow(32, 16); $i++) { $key = genKey($i); $result = openssl_decrypt(hex2bin('1fbf2605f954fad3ba18115000735aee'), 'aes-128-cbc', $key, 1, '0000000000000000'); } |
21
seth19960929 OP @8355 这个是,敏感操作已经和是 token 还是 jwt 没关系
@wunonglin 你说得对 @Masoud2023 sessionid 存取 1000w 数据, 没有哪家的黑名单有 1000w 吧? 并且我可以在刷新 token 的时候再查 redis, 而不是每次请求. |
22
nothingistrue 2023-06-19 14:29:41 +08:00 1
JWT 全称是 Javascript Web Token 。它与传统 Token 的唯一区别就是它采用非对称加密的方式。这有两个好处:一,可以自验证;二,可以携带更多的信息(传统 Token 采用 MD5 、SHA1 这样的散列加密方式,这是无法自验证和反解码的)。
JWT 只是一个高级的 Token 而已,并不是会话 /用户跟踪方案,在会话跟踪方案上讨论 JWT 的优劣根本毫无意义。而 2 楼这种比较,是想把 Token 、会话跟踪、认证(用户)、授权(权限)糅合到一起去考虑,神仙看了都会躲。 楼主说得实质上是 Token 采用 JWT 的会话跟踪方案。在会话跟踪方案上,用 JWT 代替普通 Token 、SessionId 等,除了开发难度略增之外,是只有好处没有坏处的。 |
23
ellermister 2023-06-19 14:33:40 +08:00
@seth19960929
5 分钟的延迟并不能满足常见的需求 比如,假设我登录了一个用户,拿到 jwt token ,此时点击注销,服务端加入黑名单。然后登录过程中 token 泄露或者前端只是选择性从 localstorage 删除,本质意义上服务端还存在。 因为黑名单查询不是即时的,那我就可以在服务端通过改密码方式修改用户的密码,然后重新登录,又可以拿到一个新的 token 。(当然有人会说改密码要二步验证之类的,这是另外一种做法和产品需求范畴了) > 总之在有效期内仍可以做很多事情,或者不能使其立即下线,就有很多的安全隐患。 有人说你这个强依赖于状态, 不能用无状态做, 那可能确实。但目前的大多数产品提出的需求或者我做过的产品,基础都要满足我所说的即时性。(注销了这个 KEY 就无效了, 在任何层面上他能操作能认证就是一个 BUG ) 我看了楼上,思考过确实对用户的系统不适合做 jwt ,只适合真正意义上的无状态需求(下载站鉴权、cdn 鉴权之类的、这些太少了,我基本没接触过)。而只适合所谓第三方鉴权的,服务对 服务之间的鉴权。 虽然我也在用 jwt 、而且每次请求都必命中 redis 查验黑名单,99%请求命中一次数据库。 大部分接口都需要获取用户的自身信息,为什么用户信息不全部存到 jwt ?因为不及时不可靠。 有人说你这样用 jwt 比传统 token 都麻烦,存储的还多几倍的黑名单还基本都要查 redis 、数据库,何必 jwt ,但 jwt 能够方便对接前端及第三方这种共识特性,迫使选择了 jwt 。 总之一句话,我不觉得 jwt 很好,我也觉得一身诟病。我选择 jwt 唯一的技术优势就是它只存黑名单,而传统 token 存白名单。能省点存储就省点吧。 |
24
fivesmallq 2023-06-19 14:33:48 +08:00
|
25
QlanQ 2023-06-19 15:48:03 +08:00
我是彩笔
如果多端,类似 pc + 小程序 + app + h5 想要统一一种认证方式 jwt 不是唯一选择吗? 如果多服务系统,比如 主站 Java ,活动页 php ,im go 这种,用 session 不是更麻烦吗? |
26
hsfzxjy 2023-06-19 15:55:26 +08:00 via Android
@nothingistrue 是 JSON Web Token
|
27
mxT52CRuqR6o5 2023-06-19 15:57:30 +08:00
我十分怀疑你的基于 jwt 的登录业务设计的是否正确
我没看明白强制下线功能为啥可以不是每次请求都请求 redis ,那你咋知道当前的 redis 中的 token_version 是多少 是因为你的强制下线功能不是实时的,会有 5 分钟延迟吗? |
28
mxT52CRuqR6o5 2023-06-19 15:59:06 +08:00
@ellermister #23
+1 ,安全的不完全等于完全的不安全,没见过哪家登录系统强制下线还有 5 分钟延迟的,5 分钟足够把账户里的钱偷光了 |
29
hongfs 2023-06-19 16:03:49 +08:00
@QlanQ JWT 并不是唯一,而且可能会让复杂度变高。我们现在的多端方案,同一个接口(也可以不同接口),带上不同端的 type 过来,因为不同 type 提交的内容存在差异,后端返回的是一个随机生成的 Token ( 40 位字符串)存放于 Redis ,可以做到每个端都可以有一个在线,可以做到当修改密码或者拉黑时全部端都可以一起下线。
|
30
mxT52CRuqR6o5 2023-06-19 16:04:26 +08:00
人家支付宝微信不比你的系统用户多多了,也没见人家用牺牲安全性的方法(强制踢下线有 5 分钟延迟)去提高系统性能
|
31
QlanQ 2023-06-19 16:14:47 +08:00
@hongfs 我不太能区分,jwt 和 token 的区别,在我看来原理和作用差不太多,只是说 jwt 有一个标准的规则,
如果有这种黑名单,强制下线的需求,我会将 token 存在 redis 中 然后多端登录,就是 redis ,key 的规则问题了,同样可以实现修改密码登录失效的需求 |
33
mmuggle 2023-06-19 16:45:41 +08:00 1
JWT 本身无状态,黑名单功能就是强行有状态了,但是只是针对黑名单 token ,总体来说,还是比传统 token 强一点。
|
34
seth19960929 OP @nothingistrue 说的在理
@ellermister 黑名单下线这个问题, 其实是一个取舍问题, 就是如果不能接受刷新 token 这段等待时间的话, 没什么好说的了, 就是每一次查询都走黑名单查询(其实很多系统第一次打开 app 或者重新登录才去校验), JWT 确实能省很多内存 @QlanQ 嗯嗯, token 也是一种方式, 就是随机字符串存到 redis 映射出 uid, 现在说这种存 redis 和 jwt 哪种更好. @mxT52CRuqR6o5 |
35
seth19960929 OP @mxT52CRuqR6o5 你这个话说的, 如果是和钱相关, 那和 JWT 有什么关系, 这时候不应该发短信, 人脸识别一堆校验, 这时候全面风控早就拦截好了, 你用 JWT 做这个事不合适.
|
36
seth19960929 OP |
37
hongfs 2023-06-19 17:55:39 +08:00
@seth19960929 #36 对大多数业务来说,其实 Redis 的成本也是很低的,比如你计算的 1000W key 那会有 2.4G 的一个内存使用,1000W 又不是一直在线的用户,所以内存的实际占用也是非常小的。即使你的用户在线率非常多,你需要对 Redis 进行集群化的资源成本也是非常低的。
如果非要 JWT ,其实可以做成两阶段的,用户请求先 JWT 验证,然后 sha1 等方式减少存储空间,把 sha1 的值去 redis 里面查。 |
38
QlanQ 2023-06-19 17:56:32 +08:00
@seth19960929 所以我说我分不清 jwt 和 token ,明明都是一个字符串而已,在我的认知里面,就是一个东西
|
39
seth19960929 OP |
40
daimubai 2023-06-19 18:14:10 +08:00
如果存库的话,那确实 jwt 和普通的字符串没区别。因为最终都会查库的,就算不解析 token ,也能在库中查到当前用户的信息
|
41
akira 2023-06-19 18:18:29 +08:00
那个,你们家用户都是千万级的了么。。。
|
42
GeruzoniAnsasu 2023-06-19 18:49:57 +08:00 4
看了半天在吵什么……
> 普通的 token 无任何有效信息, 只是一个唯一值, JWT 的话可以通过 秘钥 从 token 里解析出 uid 之类的, 这样子有些场景只有 uid 就不用查数据库 原来还就是车轱辘话翻来覆去讲,说到底 OP 发现的「优势」也就是 jwt 带信息,随机 token 不带信息而已,这不就是废话…… > 至于常见的 JWT 诟病解决方案 OP 的这段发散了这么多,加上他楼下的回复,其实就一句话: 退化成普通 session / token 方案 > 为什么要用 JWT ? > - 使用 token 会有千万级用户请求 * (存 token + 查 token redis)... 用户名,xxx 存储, 文不达意。 根本原因其实就是 jwt 携带的信息可以短路一部分查库判断而已 我来替 OP 总结一下他的看法: 1. 在最坏场景下,jwt 退化成普通 session/token 方案,此时主要缺陷是 jwt 数据量引入的内存浪费,但即使在极大并发量条件下 redis 的性能也完全够用,所以可以忽略不计 2. 在典型场景下,jwt 自带 session/token 方案需要查库才能获得的关联信息,如用户 id 等,在这些场合下能节省大量数据库连接资源 所以这不就是什么「初识」、「入门」文章里就提到的东西么…… |
43
hyperbin 2023-06-20 07:54:41 +08:00 via Android
@LeegoYih If you are very lucky or have a huge computing power ,相当于说国库不安全,如果你有灭国级的实力的话
|
44
seth19960929 OP @daimubai 查一次和一万次能一样吗?这样子说的话,干嘛架构这么多,干嘛用这么多缓存,直接查数据库了
@GeruzoniAnsasu 一直强调更多的怎么去解决 jwt 的问题,我后面说的这么多黑名单,刷新的机智话说都无视了吗 @akira 这是一个讨论贴,可以直接 block |
45
QlanQ 2023-06-20 09:01:02 +08:00
@seth19960929
嗯嗯, token 也是一种方式, 就是随机字符串存到 redis 映射出 uid, 现在说这种存 redis 和 jwt 哪种更好. 本质都是一个字符串是吧,jwt 、token 傻傻分不清 |
46
seth19960929 OP @QlanQ 对, json web token 从名字看出来它也是一种 token
|
47
daimubai 2023-06-20 11:16:51 +08:00
@seth19960929 #44 查一万次是怎么得来的?
|
48
daimubai 2023-06-20 11:20:52 +08:00
@GeruzoniAnsasu 他都不知道自己在说些什么
|
49
chendy 2023-06-20 11:54:04 +08:00
JWT 适合自家服务之间通信,网关授权之后带上 JWT 请求后续服务,后续服务不需要再查用户权限之类的东西,降低用户中心服务的负载
客户端还是适合最朴素的 token 模式( cookie 里的 sessionid 其实也是一种 token ) |
50
seth19960929 OP |
51
v2Geeker 2023-06-21 09:20:56 +08:00 via iPhone
我们公司是全线用的 OIDC 那一套,当然也就用了 JWT 。这套像介于传统 Session 和 无状态 JWT 之间,服务端和客户端双赢。
|
53
8rmEHZ8WhVHVOb0E 2023-10-07 00:34:43 +08:00
你说的方案并没有节省 Redis 查询次数,只省略了 redis 内存而已,但是:
Jwt 方案 token 长度往往有数百个字符串,并且根据 Payload 内容的增多而变的更长,每次请求都会把这些东西带上。 而类 session 模型的 token ,token 字符串长度往往控制在几十个字符串长度。 假设每个平均每个 jwt token 长度 250 个字符串,而 session 模式的 token 长度 50 个字符串,那么每个请求就多了 200 个字符串,如果你的网站每秒 1 万 QPS ,你的服务器带宽峰值大约会提高 40Mb 40Mb 带宽明显比几 G redis 内存要贵鸭 |
54
seth19960929 OP @xiaomada 服务器的带宽一般只计传输给客户端的, 不计客户端上传
|
55
seth19960929 OP @xiaomada 请看原文, 只有当 refresh 的时候才去查询 redis. 至少减少 99% 的查询 redis 次数.
|