JWT 服务端怎么理解不需要存储 session

2019-02-18 19:27:43 +08:00
 Lateautumn

接触了一下 jwt,很多说法说服务端是无状态的,我这里有个疑问,当客户端是发送 token 过来 ,服务端怎么验证这个 token 的真实性,以及是哪个用户,如果把用户信息放在 payload 里面的话可以,那怎么去验证这个呢,不是还得去数据库查吗,如果不验证怎么知道是哪个用户?

8816 次点击
所在节点    Python
66 条回复
rayingecho
2019-02-19 10:35:55 +08:00
@tabris17 你说得没错,但有两点可供权衡:
1: jwt 的 payload 大部分不需要存储在 redis 里,因为可以用签名来验证,真正需要的只有一个 uuid ;而 session 共享要全都存储。存储成本小
2: jwt 只需要判断一 exist,session 共享需要 get。带宽压力小
当然这里不是说 session 共享不行,jwt 也有很多问题,比如更新 payload 字段需要重新签发,浏览器不会自动发送 Authorization header
另外,我认为不存在绝对的不可接受(也不存在完美的架构),存在的只有针对自身场景的合理权衡,有时候需要两害相权取其轻
tabris17
2019-02-19 10:47:58 +08:00
@rayingecho 恩,JWT 适用的场景只有一次性鉴权,不是身份认证,而是操作的令牌
yamedie
2019-02-19 10:54:24 +08:00
前端进来插句嘴, 如果按照#2 所说的那样, 只认钥匙(token)不认人(userId), 应该如何解决水平权限问题? 即: 正常登录有合法 token 的用户, 如何防止他调用接口查阅其他用户的隐私数据? token 要和 userId 进行关联是吗, 那这样还能认为是无状态吗?
d5
2019-02-19 10:58:22 +08:00
@yamedie #23 比如可以在 jwt token 中新增 path 字段,规定该 token 仅仅能够访问的白黑名单
loading
2019-02-19 11:00:11 +08:00
其实就是只认 token 那个字符串,如何关联这个 token 就看你了。
rayingecho
2019-02-19 11:00:24 +08:00
@tabris17
“只有一次性”有点绝对了,jwt 只是一个简单的标准,不是一个完整的方案,只要能正确理解并应用,放到 app 端身份认证或者 oauth 授权(可持续使用)体系里都可以工作的很好。其实我想讲的还是根据具体场景去权衡,尽量避免 pre-judge
yamedie
2019-02-19 11:03:16 +08:00
@d5 不, 可能我没描述清楚. 比如 userId=1 的用户正常登录得到了 token=11111, 他调用"我的账户"接口, 但篡改了 post 请求里的 userId->2, token 合法, 接口合法, 如何防止他看到 2 号用户的个人信息? 如果有无数个类似的接口, 如何鉴别 token 和传入的 userId 是否对应同一个用户?
yamedie
2019-02-19 11:05:35 +08:00
@yamedie 如何"优雅地"鉴别 token 和传入的 userId 是否对应同一个用户?
yamedie
2019-02-19 11:06:18 +08:00
@yamedie 也许不传入 userId 是最优雅的....
fakeshadow
2019-02-19 11:11:05 +08:00
@yamedie jwt 的 payload 里放入 userId,用户的 post 进来再从 payload 里读。
buhi
2019-02-19 11:14:36 +08:00
为什么我修改了密码就要重新登录? 不是很奇怪吗... 老子前几秒才输入了一遍密码 又让我输入一遍
rayingecho
2019-02-19 11:16:12 +08:00
@yamedie
其实 #2 说的是一个宽泛的 token 比喻,所有的 token 都可以为获得某种权限的钥匙

但太泛化就会失去细节,jwt 更适合比喻为一纸身份证明,我在上面写上你是叫张三,你允许使用健身房和游泳池(这些叫 payload ),然后啪敲一个大公章(签名)盖住这些字

到时候无论谁拿着这张证明( jwt )过来,我都认为他是张三并且可使用健身房游泳池。假如这个人心眼坏,把张三加一竖改成了“张王”,那这一竖就会涂在公章上破坏了签名,这纸证明就无效了
123s
2019-02-19 11:18:26 +08:00
只是带个 token 而已,里面会带个 id,访问的时候会查有没有过期
yamedie
2019-02-19 11:20:32 +08:00
@rayingecho 我明白了, 我司的后端用了 jwt token, 同时在定义"我的订单", "我的账户"这类接口时, 还要求前端传入 userId, 并未在 token 里关联 userId, 造成水平权限漏洞
fundebug
2019-02-19 11:25:27 +08:00
est
2019-02-19 11:39:53 +08:00
jwt 跟 cookie 里放一个巨大的 base64 字符串是一样的作用

无非就多了个签名姿势而已。。。
honeycomb
2019-02-19 11:48:37 +08:00
@glaucus redis 里存一份和 token 一一对应的信息。
token 里的非标准字段可以存放一些能帮助你达到目的的额外信息?
smallthing
2019-02-19 11:55:45 +08:00
其实就是个高级 cookie 明白了?
passerbytiny
2019-02-19 12:01:44 +08:00
首要要了解令牌、认证、授权,然后再看下文。注意,在中文中认证、授权有时候是名词,有时候是动词,需根据上下文判断,它们在英文术语中动词和名词是不同的单词。

先说结论,JWT 不能替代 Session,只能替代 Session 的部分功能,即认证。

JWT 是作为令牌设计的。如果只是入门使用,那么设计的是低级令牌,有这些特点:一、需要预先申请;二、不可伪造,但可复制;三、令牌是唯一认证手段,见牌如见人;四、有有效期,但不可收回;五、可以负载其它信息,包括授权。低级令牌有这些漏洞:可以被复制;可以被冒用;不可回收。另外还有一个大漏洞,直接在令牌上放授权,不过一般没人这么干,所以不讨论这个漏洞了。如果你回用 JWT,那么这些漏洞都可以解决。

关于可以被复制的漏洞,最简单的解决方案就是负载中包含客户端 IP,认证时增加额外认证“令牌中 IP=客户端 IP ”。大部分情况下客户端 IP 是不可复制的,如果需要更高安全度,那么就增加更多的不可复制信息,比如 APP 上的设备识别号。作为对比,Session 方式同样有该漏洞,而且复制更简单,只要复制 Header 就够了。而 Session 方式是不能独自解决该漏洞的。

关于可以被冒用的漏洞,这个漏洞单靠 JWT 或 Session 都是无法解决的,只能通过二次验证、行为分析等其它方式去解决。

关于不可回收的漏洞,有两种解决方式,一种是 JWT 认证+辅助认证方式,一种是纯 JWT 认证方式,前者更有效和更简单但有性能损耗。JWT 认证+辅助认证方式是这样,使用 Redis 或数据库维护一份以发出的令牌为键,以用户 ID 为索引,以有效性为值的表,在认证令牌的同时,认证令牌的有效性。如果不考虑性能的话,还可以更简单,辅助认证方式改成直接去数据库查询该令牌代表的用户是否还有效。纯 JWT 认证方式,就是令牌的有效期设置的特别短,但每次认证后都向客户端发放新的令牌。作为对比,第二种认证方式等同于 Session 方式,第一种认证方式优于第二种方式和 Session 方式。

最后,如果深入研究 Session,你就会发现,在认证阶段,Session 本质上也是令牌。但是 Session 不只具有认证的功能,它还有会话存储的功能,这点 JWT 是没有的,所以可以结合使用 JWT 和 Session。比如用 JWT 做认证,用 Session 保存授权,当然还是建议用 JWT 做认证,用 Redis 等缓存来保存授权。
jadeity
2019-02-19 12:11:40 +08:00
@buhi 当你密码有泄漏风险的时候就不这么想了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/536256

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX