(译)别再使用 JWT 作为 Session 系统!问题重重且很危险。

JSON Web Tokens,又称 JWT。本文作者将详解:为何 JWT 不适合存储 Session,以及 JWT 引发的安全隐患。望各位使用前三思。

原文地址:http://cryto.net/~joepie91/blog/2016/06/13...
断断续续翻译了一周,今天终于审校一下,算是填坑完毕。自己博客广告走一波:https://wi1dcard.cn/

十分不幸,我发现越来越多的人开始推荐使用 JWT 管理网站的用户会话(Session)。在本文中,我将说明为何这是个非常非常差劲的想法。

为了避免疑惑和歧义,首先定义一些术语:

  • 无状态 JWT(Stateless JWT):包含 Session 数据的 JWT Token。Session 数据将被直接编码进 Token 内。
  • 有状态 JWT(Stateful JWT):包含 Session 引用或其 ID 的 JWT Token。Session 数据存储在服务端。
  • Session token(又称 Session cookie):标准的、可被签名的 Session ID,例如各类 Web 框架(译者注:包括 Laravel)内已经使用了很久的 Session 机制。Session 数据同样存储在服务端。

需要澄清的是:本文并非挑起「永远不要使用 JWT」的争论 —— 只是想说明 JWT 并不适合作为 Session 机制,且十分危险。JWT 在其它方面的确有其用武之地。本文结尾,我将简短地介绍一些合理用途。

首先需要说明#

很多人错误地尝试比较 CookiesJWT。这种对比毫无意义,就像对比内存和硬盘一样。Cookies 是一种存储机制,然而 JWT Tokens 是被加密并签名后的令牌。

它们并不对立 —— 相反,他们可以独立或结合使用。正确的对比应当是:Session 对比 JWT,以及 Cookies 对比 Local Storage

在本文中,我将把 JWT Tokens 同 Session 展开对比,并偶尔对比 CookieLocal Storage。这样的比较才有意义。

JWT 坊间流传的优势#

在人们安利 JWT 时,常常宣扬以下几点好处:

  • 易于水平扩展
  • 易于使用
  • 更加灵活
  • 更加安全
  • 内置过期时间功能
  • 无需询问用户「本网站使用 Cookies」
  • 防止 CSRF 攻击
  • 更适用于移动端
  • 适用于阻止 Cookies 的用户

我将会逐条阐述以上观点为何是错误或误导性的,其中部分解释可能会有些模糊,这主要是因为这些「好处」的表述本身就比较模糊。你可以在文末找到我的联系方式,我将十分乐意对更加具体的「好处」进行分析阐述。

易于水平扩展?#

这是列表中唯一一条在技术层面部分正确的「好处」,但前提是你使用的是无状态 JWT Tokens。然而事实上,几乎没人需要这种横向扩展能力。有很多更简单的拓展方式,除非你在运维像淘宝这样体量的系统,否则根本不需要无状态的会话(Stateless sessions)。

一些扩展有状态会话(Stateful sessions)的例子:

  1. 在单台服务器上运行多个后端进程:只需在此服务器上安装 Redis 服务用于存储 Session 即可。
  2. 运行多台服务器:只需一台专用的 Redis 服务器用于存储 Session 即可。
  3. 在多集群内运行多台服务器:会话保持(又称:粘滞会话)。

以上所有场景在现有软件系统内都具备良好的支持,你的应用需要进行特殊处理的可能性基本为零。

或许你在想,应当为你的应用预留更多调整空间,以防未来需要某些特殊操作。但实践告诉我们,以后再替换 Session 机制并不困难,唯一的代价是,在迁移后所有用户将被强制登出一次。我们没必要在前期实现 JWT,尤其是考虑到它所带来的负面影响。我将在后文进行解释。

易于使用?#

这个真没有。你不得不自行处理 Session 的管理机制,无论是客户端还是服务端。然而标准的 Session cookies 则开箱即用,JWT 并没有更简单。

更加灵活?#

我暂时还没看到有人成功地阐述「JWT 如何更加灵活」。几乎每个主流的 Session 实现,都允许你直接把数据存储进 Session,这跟 JWT 的机制并没有差别。据我所知,这只是个流行语罢了。如果你不同意,可以随时带上示例与我联系。

更加安全?#

一大批人认为 JWT Tokens「更加安全」,理由是使用了加密技术。实际上,签名后的 Cookies 比未签名的 Cookies 同样更加安全,但这绝不是 JWT 独有的,优秀的 Session 实现均使用签名后的 Cookies(译者注:例如 Laravel)。

「使用加密技术」并不能神奇地使某些东西更加安全,它必须服务于特定目的,并且是针对该目的的有效解决方案。错误地使用加密反而可能会降低安全性。

另一个我听过很多次的对于「更加安全」的论述是「JWT 不使用 Cookies 传输 Tokens」。这实在是太荒谬了,Cookie 只不过是一条 HTTP 头信息,使用 Cookies 并不会造成任何不安全。事实上,Cookies 受到特别良好的保护,用于防止恶意的客户端代码。我将在后文进行阐述。

如果担心有人拦截掉你的 Session cookies,那你应当考虑使用 TLS。如果不使用 TLS,任何类型的 Session 机制都可能被拦截,包括 JWT。

内置过期时间功能?#

无意义,又没什么卵用的特性。在服务端也能实现过期控制,有不少 Session 实现就是这么做的。实际上,服务端的过期控制更加合理,这样你的应用就可以清除不再需要的 Session 数据;若使用无状态 JWT Tokens 且依赖于它的过期机制,则无法执行此操作。

无需询问用户「本网站使用 Cookies」?#

完全错误。并没有什么「Cookies 法律」—— 有关 Cookies 的各种法律实际上涵盖了任何类型「对某项服务的正常运行非严格必须的持久性 ID」,任何你能想到的 Session 机制都包括在内。

译者注:然鹅中国并没有。

简单来说:

  • 若出于系统功能目的使用 Session 或 Token(例如:保持用户的登录态),那么无论怎样存储 Session 均无需征得用户同意。
  • 若出于其他目的使用 Session 或 Token(例如:数据分析、追踪),那么无论怎样存储 Session 都需要询问用户是否允许。

防止 CSRF 攻击?#

这个真・真没有。存储 JWT Tokens 的方式大概有两种:

  • 存入 Cookie:仍然易受 CSRF 攻击,还是需要进行特殊处理,保护其不受攻击。
  • 其他地方,例如 Local Storage:虽然不易受到 CSRF 攻击,但你的网站需要 JavaScript 才能正常访问;并且又引发了另一个完全不同,或许更加严重的漏洞。我将在后文详细说明。

预防 CSRF 攻击唯一的正确方法,就是使用 CSRF Tokens。Session 机制与此无关。

更适用于移动端?#

毫无根据。目前所有可用的浏览器几乎都支持 Cookies,因此也支持 Session。同样,主流的移动端开发框架以及严谨的 HTTP 客户端库都是如此。这根本不是个问题。

适用于阻止 Cookies 的用户?#

不太可能。用户通常会阻止任何意义上的持久化数据,而不是只禁止 Cookies。例如,Local Storage 以及任何能够持久化 Session 的存储机制(无论是否使用 JWT)。不管你出于多么简单的目的使用 JWT 都无济于事,这是另一个完全独立的问题了。另外,试图让身份认证过程在没有 Cookies 的情况下正常进行,基本没戏。

最重要的是,禁用掉所有 Cookies 的多数用户都明白这会导致身份认证无法使用,他们会单独解锁那些他们比较关心的站点。这并不是你 —— 一个 Web 开发者应当解决的问题。更好的方案是,向你的用户们详细地解释为何你的网站需要 Cookies 才能使用。

JWT 的劣势#

以上,我已经对常见的误解做了说明,以及为什么它们是错误的。你或许在想:「这好像也没什么大不了的,即便 JWT 无法带来任何好处,但也不会造成什么影响」,那你真是大错特错了。

使用 JWT 作为 Session 机制存在很多缺点,其中一部分会造成严重的安全问题。

更费空间#

JWT Tokens 实际上并不「小」。尤其是使用无状态 JWT 时,所有的数据将会被直接编码进 Tokens 内,很快将会超过 Cookies 或 URL 的长度限制。你可能在想将它们存储到 Local Storage,然而...

更不安全#

若将 JWT Tokens 存储到 Cookies 内,那么安全性与其他 Session 机制无异。但如果你将 JWT 存储至其它地方,会导致一个新的漏洞,详见本文,尤其是「Storing sessions」这一部分。

书接上回:Local Storage,一个 HTML5 内很棒的功能,使浏览器支持 Key/Value 存储。所以我们应当将 JWT Tokens 存储到 Local Storage 吗?考虑到这些 Tokens 可能越来越大,或许会很有用。Cookies 通常在 4k 左右的存储时比较占优势,对于较大的 Tokens,Cookies 可能无法胜任,而 Local Storage 或许成了明确的解决方案。然而,Local Storage 并没有提供任何类似 Cookies 的安全措施。
LocalStorage 与 Cookies 不同,并不会在每次请求时发送存储的数据。获取数据的唯一方法是使用 JavaScript,这意味着任何攻击者注入的 JavaScript 脚本只需通过内容安全策略检查,就能任意访问或泄露数据。不光是这样,JavaScript 并不在意或追踪数据是否通过 HTTPS 发送。就 JavaScript 而言,它就只是个数据而已,浏览器会像操作其它数据一样来处理它。
在历代工程师们经历了各种麻烦之后,终于能够确保没有人可以恶意接触到我们的 Cookies,然而我们却试图忽略这些经验。这对我来说似乎是在退步。

简单来说,使用 Cookies 并不是可选的,无论你是否采用 JWT。

无法单独销毁#

还有更多安全问题。不像 Sessions 无论何时都可以单独地在服务端销毁。无状态 JWT Tokens 无法被单独的销毁。根据 JWT 的设计,无论怎样 Tokens 在过期前将会一直保持有效。举个例子,这意味着在检测到攻击时,你却不能销毁攻击者的 Session。同样,在用户修改密码后,也无法销毁旧的 Sessions。

对此,我们几乎无能为力,除非重新构建复杂且有状态(Stateful)的基础设施来明确地检测或拒绝特定 Session,否则将无法结束会话。但这完全违背了使用无状态 JWT Tokens 的最初目的。

数据延迟#

与上文的安全问题类似,还有另一个潜在的安全隐患。就像缓存,在无状态 Tokens 内存储的数据最终会「过时」,不再反映数据库内最新的数据。

这意味着,Tokens 内保留的可能是过期的信息,例如:用户在个人信息页面修改过的旧 URL。更严肃点讲,也可能是个具备 admin 权限的 Token,即使你已经废除了 admin 权限。因为无法销毁这些 Tokens,所以面对需要移除的管理员权限,除非关闭整个系统,别无他法。

实现库缺乏生产环境验证或压根不存在#

你或许在想,以上的这些问题都是围绕着「无状态 JWT」展开的,这种说法大部分情况是对的。然而,使用有状态 Tokens 与传统的 Session cookies 基本上是等效的... 但却缺乏生产环境的大量验证。

现存的 Session 实现(例如适用于 Express 的 express-session)已经被用于生产环境很多很多年,它们的安全性也经过了大量的改良。倘若使用 JWT 作为 Session cookies 的临时替代品,你将无法享受到这些好处,并且必须不断改进自己的实现(在此过程中很容易引入漏洞),或使用第三方的实现,尽管还没有在真实世界里大量应用。

译者注:实际上,Laravel Passport 便是使用类似「有状态 JWT」的方式来存储 OAuth Access Token。幸运的是,Passport 已经有不少实际应用,且不完全依赖于 JWT。

结论#

无状态 JWT Tokens 无法被单独地销毁或更新,取决于你如何存储,可能还会导致长度问题、安全隐患。有状态 JWT Tokens 在功能方面与 Session cookies 无异,但缺乏生产环境的验证、经过大量 Review 的实现,以及良好的客户端支持。

除非,你工作在像 BAT 那样规模的公司,否则没什么使用 JWT 作为 Session 机制的理由。还是直接用 Session 吧。

所以... JWT 适合做什么?#

在本文之初,我就提到 JWT 虽然不适合作为 Session 机制,但在其它方面的确有它的用武之地。该主张依旧成立,JWT 特别有效的使用例子通常是作为一次性的授权令牌。

引用 JSON Web Token specification

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. [...] enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.

在此上下文中,「Claim」可能是一条「命令」,一次性的认证,或是基本上能够用以下句子描述的任何情况:

你好,服务器 B,服务器 A 告诉我我可以 <...Claim...>,这是我的证据:< ... 密钥... >。

举个例子,你有个文件服务,用户必须认证后才能下载文件,但文件本身存储在一台完全分离且无状态的「下载服务器」内。在这种情况下,你可能想要「应用服务器(服务器 A)」颁发一次性的「下载 Tokens」,用户能够使用它去「下载服务器(服务器 B)」获取需要的文件。

以这种方式使用 JWT,具备几个明确的特性:

  • Tokens 生命期较短。它们只需在几分钟内可用,让客户端能够开始下载。
  • Tokens 仅单次使用。应用服务器应当在每次下载时颁发新的 Token。所以任何 Token 只用于一次请求就会被抛弃,不存在任何持久化的状态。
  • 应用服务器依旧使用 Sessions。仅仅下载服务器使用 Tokens 来授权每次下载,因为它不需要任何持久化状态。

正如以上你所看到的,结合 Sessions 和 JWT Tokens 有理有据。它们分别拥有各自的目的,有时候你需要两者一起使用。只是不要把 JWT 用作 持久的、长期的 数据就好。

本作品采用《CC 协议》,转载必须注明作者和本文链接
Former WinForm and PHP engineer. Now prefer Golang and Rust, and mainly working on DevSecOps and Kubernetes.
本帖由系统于 6年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 65
Mangou

JWT 在作为 session 时 , 自动续期和指定失效 2 个方面是致命伤。

laravel passport 还有维护 token 黑名单这种操作。为了用而用。。。

6年前 评论

@Flourishing

目前来说,我博客使用的是 Hexo + indigo 主题。

另外涉及到几个点:

6年前 评论
Mangou

JWT 在作为 session 时 , 自动续期和指定失效 2 个方面是致命伤。

laravel passport 还有维护 token 黑名单这种操作。为了用而用。。。

6年前 评论

可以大概讲讲你的博客的架构吗? 我想做一个类似的博客站点

6年前 评论

@Flourishing

目前来说,我博客使用的是 Hexo + indigo 主题。

另外涉及到几个点:

6年前 评论

好文章,不错。

6年前 评论

如何作废已颁发的 JWT 确实是个问题

6年前 评论
zgxxx

一些无法使用 cookie 的场景还是得用 JWT 的,像第三方接口,例如小程序开发我就是用 jwt

6年前 评论

@zgxxx

很多人错误地尝试比较 Cookies 和 JWT。这种对比毫无意义,就像对比内存和硬盘一样。Cookies 是一种存储机制,然而 JWT Tokens 是被加密并签名后的令牌。
它们并不对立 —— 相反,他们可以独立或结合使用。正确的对比应当是:Session 对比 JWT,以及 Cookies 对比 Local Storage。
在本文中,我将把 JWT Tokens 同 Session 展开对比,并偶尔对比 Cookie 和 Local Storage。这样的比较才有意义。

建议认真阅读。

6年前 评论
zgxxx

@Wi1dcard “一些无法使用 cookie 的场景还是得用 JWT”,这里并没有比较 cookie 和 JWT,session 最终也是归到 cookie 里面,只不过服务器客户端问题

6年前 评论

@zgxxx JWT 是一种存储介质,无法使用 Cookie 的场景,例如你说的微信小程序,照样可以使用 Local Storage,那么照样可以存储 Session ID,这完全不需要 JWT。同样,你使用 JWT 照样可以将它的 Token 存储在 Cookies 内,你根本没有搞清楚这几个东西的关系。

6年前 评论

@Will

使用 token 传递识别身份,难道有错了。。微信开放平台不是也是 key 与 secret 换取 ticket. 然后获取 token 吗。续期可以使用 refreshtoken 进行刷新,刷新后 之前的 token(没有过期的)可以放在黑名单中,然后过期也自动失效了。

辩:JWT Tokens 不等于 Tokens。你这里说的 Tokens 是 OAuth 机制内的 Tokens,跟 JWT 无关,请你搞清楚再来反驳。

比如禁用 cookie. 可以把生成的 JWT 放在头信息。或者放在 QueryString 中。只是使用 token . 至于怎么携带 是使用 cookie 携带、还是使用自定义的 http 头部携带。这个都不是问题。。你可以反驳说可以把类似的 session_id PHPSESSID 放在 QueryString 和自定义的头部一样,JWT 只是多了一种 http 上下文生成唯一标示符号 token 多了一种实现。

辩:把 Token 放在 QueryString 或自定义 HTTP 头内发送,也就意味着必须将 Token 存储到 LocalStorage 或其它地方,那么原文已经解释过会出现另一种漏洞:若将 JWT Tokens 存储到 Cookies 内,那么安全性与其他 Session 机制无异。但如果你将 JWT 存储至其它地方,会导致一个新的漏洞,详见本文,尤其是「Storing sessions」这一部分。

你翻译一篇文章 就反驳所有的。这篇文章也只是作者的建议。

辩:英文水平好可以去看原文,原作者的语气自己揣摩。或许我翻译的不好,你也可以来翻译或者改进。对吧。

最后,不服欢迎辩论,但不要引战,就像原文在最初提到的:

需要澄清的是:本文并非挑起「永远不要使用 JWT」的争论 —— 只是想说明 JWT 并不适合作为 Session 机制,且十分危险。JWT 在其它方面的确有其用武之地。本文结尾,我将简短地介绍一些合理用途。

6年前 评论

@Will

如果 JWT 极端使用。。就可以放在每个链接上(或者说每个 api 中),鉴权完后下发 token, 每个需要授权的链接都带上 JWT 的 QueryString(或者 http 头部的某个 key). 服务端在 QueryString(或者 http 头部的某个 key)获取到这个 token. 然后在返回 token . 客服端自动拼接 token 到 QueryString(或者 http 头部的某个 key)中。这样就可以不需要保存 token 而在每次请求中带上这个 token。虽然并不推荐这样做,这样做也不好,但是也可以做到本地不保存 token, 而在每个需要带上 token 的地方使用 token, 万事无绝对。

辩:也就是说,你将 Token 拼接为 URL 直接下发给客户端,那么通过 JavaScript 照样能够读出来 Token 值,并且可以被另作他用。恶意代码攻击依旧可行。

有说微信的 token 就是 JWT... 呵呵。。

辩:请仔细阅读我的观点,另外请纠正你的错别字。

比如:服务器之间的鉴权交互(分布式系统之间的交互等),不一定 JWT 一定就要使用客户端、服务端的交互,通过鉴权后生成 JWT,token 就可以放在服务端,不一定非要什么 cookie LocalStorage, 可以放在服务端任何可以存储的地方,比如 redis、内存等。

辩:是的,无论是我还是原文作者,都没有彻底贬低 JWT,在本文最初和结尾都有强调「JWT 在其它方面有合适的用武之地,它只是不适合作为 Session 机制」,本文标题也是「请勿使用 JWT 作为 Session 机制」。而且结尾,作者也推荐了与你描述的这种用途类似的场景。请你仔细阅读完本文,练习好自己的表达能力和理解能力,再发表你的观点。

不再辩解,你说的都有理。谢谢~~

可笑。

6年前 评论

@史沟飞 想作废还是有多方法的。jwt 配合服务器产生登录用户名单,服务端删除需要重新登录校验不就可以了。或者指定过期时间,之后拿到密码从新生成 token,不一致就过期呗。

6年前 评论

@Rain 是的,但请注意这一句:

对此,我们几乎无能为力,除非重新构建复杂且有状态(Stateful)的基础设施来明确地检测或拒绝特定 Session,否则将无法结束会话。但这完全违背了使用无状态 JWT Tokens 的最初目的。

6年前 评论

@Wi1dcard 后端是 laravel 架构的吗?

6年前 评论

@Flourishing 没有后端,纯静态,使用 Hexo 作为静态站点生成器渲染出的 HTML。

如果想要 Laravel 的 Blog,并且喜欢定制学习的话,推荐一个我同事的 Repo:https://github.com/baijunyao/laravel-bjybl...

6年前 评论
萧瑟秋风 5年前

@Wi1dcard 谢谢你的分享 功能还是很丰富的,就是看起来样式不是那么炫酷

6年前 评论

@Flourishing 所谓 Functional beauty,哈哈。

6年前 评论
yourself

挺好的,jwt 什么的其实并不是重点。使用技术完全取决于你的公司,但是技术选型一些周边技术分析的考虑因素这才是关键。例:优势,缺点,安全性,使用场景

6年前 评论
巴啦啦

我在做安卓后端的时候,我有过这样的做法。在 laravel 中做两个中间件,一个是响应颁发 token,一个是收到请求验证 token。响应时,自动续时 token。收到请求时,验证 token 是否正确,有没有过期。我是这么实现 token 的。

实现一个数组,数组内包含用户基本信息和过期时间戳。然后转换为 json,json 后面拼接一个 env 文件里的自定义的串。

最后将串通过某种自定义的编码方法转换为一个看起来无意义的串 (这个编码方法里面也会有 env 文件里的自定义串)。对应的还有解码方法。编码和解码都做成了一个 trait,分别在进出两个中间件中使用。

我觉得还是 session 香。

6年前 评论

那不对哦,文中说了很多 Storage+jwt 不如 cookie+session, 但是 app 没 cookie 的

6年前 评论

@gangpula 谁说 App 不支持 Cookies。

6年前 评论
panda-sir

说下我的观点: 更费空间 : 我在设计 jwt token 存储的时候 只存入了关键信息 无状态 token 不代表 需要存入用户所有基本信息
无法单独销毁: 我将 token 按找一定的规律存储在我的 redis 中 可以达到随时销毁的目的 销毁后 虽然前端 cookie 内存储的 token 没有销毁 但当用户再次访问系统的时候 api 会抛出对应的状态码 并没有你说的无法单独销毁的问题 主要看你怎么设计存储 token
数据延迟 : 你在文中提的这个权限的 token 例子很有代表性 但我只能说明 我使用 jwt token 的时候只是为了维持用户登录状态 权限信息是根据 token 内解析的用户 id 获取的 token 的就算不销毁 对应用户的权限一旦关闭 你也没法进入对应的系统或者模块的
实现库缺乏生产环境验证或压根不存在: 这个嘛 勇于尝试 发现问题 解决问题 才能让这项技术让更多的人知晓
最后再提一句 防止 csrf_token 攻击是你需要将 token 以 http 请求首部发送 而不是存到 cookie 然后发送 默认浏览器都会携带 cookie 你只要不使用前端传递的 cookie 使用跟前端约定的 http 首部字段 就可以避免 csrf_token 问题 因为你已经不使用 cookie 了

6年前 评论

@panda-sir

我将 token 按找一定的规律存储在我的 redis 中 可以达到随时销毁的目的 销毁后 虽然前端 cookie 内存储的 token 没有销毁 但当用户再次访问系统的时候 api 会抛出对应的状态码 并没有你说的无法单独销毁的问题 主要看你怎么设计存储 token

将 Token 写入 Redis,首先这个操作就完全丧失了 JWT 存在的意义,与 Session 机制无异。且 JWT 的 Token 通常会比传统 Session ID 更加长,而你这个方案还需要在客户端和服务端「同时」存储这个比较长的 Token。虽可行,但也只能算是折中吧,跟维护一份 Token 黑名单类似。

使用跟前端约定的 http 首部字段

那就导致你需要将 Token 存储到类似 Local Storage 的地方,因此又引发了另一个漏洞,在文中有说明。

6年前 评论
panda-sir

@Wi1dcard 受教了 查了查资料
清空或修改服务端的用户对应的 secret,这样在用户注销后,jwt 本身不变,但是由于 secret 不存在或改变,则无法完成校验。这也是为什么将 secret 设计成和用户相关的原因。
仔细看了下 感觉也不能解决无法销毁 Token 的问题,当用户在多个终端或者 单个终端多个浏览器登录的时候,会因为改变用户的 secret 使得所有终端或者所有 Token 都失效,是否有更好的办法呢 如果对 Token 不存储在服务端的话

6年前 评论

@panda-sir 是的,会直接全部失效。

至于完全无状态(即服务端完全没有 Token 等运行时写入的数据),个人认为比较难。可以考虑 Kubernetes 等,将应用服务独立出来无状态,便可随时横向扩展集群;而 Session 服务由独立的 Redis 集群等提供。

6年前 评论
panda-sir

@Wi1dcard :crying_cat_face: 这还是 session 存储服务端管理登录状态 按照 jwt 设计的初衷来讲 不存储服务端的话,感觉也许这就是 http 吧

6年前 评论
guanhui07

好文。

6年前 评论
hookover

无法销毁应该是有替代手段的,比如二次校验
或者增加用户的个人 KEY 到算法中,修改密码的同时重置该 KEY 即可

6年前 评论

新手请教,不用 jwt 的话前后端分离项目怎么做呀,前后端不同域名,怎么用 session 呢?
前端把接口返回的 sessionid 保存到 cookie 里么?那这样的话 ajax 访问后端接口会自动带着 cookie 里的数据访问(sessionid)接口吗?

  • -,新手一枚真心不懂木有头绪,望高手大大们不吝赐教,帮我详细说一下哈。。学习 ing~!!
6年前 评论

@hookover 按照你的想法,这样需要每次请求都验证一遍这个假定的 Key,那么该 Key 存储在哪里呢?数据库吗?那与「每次请求验证用户密码」有什么本质上的区别呢?

6年前 评论

@牛牛小宝 前后端不同域名并不影响 Session。只要 确保签发 Session ID 时的域名请求后端时 的域名一致,浏览器会自动将存储在 Cookies 内的 Session ID 携带并发送。

6年前 评论

我个人觉得 session 本身存在的意义不大,尤其在如今前后端分离成为趋势的情况下。

6年前 评论

@Wi1dcard 感谢回答。这个问题大概明白了,ajax 请求时设置 withCredentials=true 就可以跨域携带 cookie 信息了。
那如果像小程序没有 cookie 怎么处理 sessionid 的传递呢?

6年前 评论

@牛牛小宝 我个人猜测小程序禁止 Cookies 的存在是为了避免大量无用 Cookies 的存在拖慢请求速度。至于如何解决这个问题,我对小程序的具体内部机制不太清楚,不过依稀记得似乎是采用 LocalStorage 来解决这个问题,在每次请求的时候手动带上 SessionID(或者是 Token)。实际上因为小程序并不是标准的浏览器环境,我猜微信对小程序有进行特殊的隔离处理,所以存储在 LocalStorage 内的数据 应该 是安全的。

6年前 评论

@Rain 感觉这样的话就几乎是自己实现了一遍 session。

6年前 评论

请教作者,如果我在 app 里需要访问 API , 在鉴权方面,我们之前用的是 JWT token,您觉得是否合适?如果不合适,应该用什么?请指条明路!谢谢

6年前 评论

两者的使用场景不尽相同,jwt 跟 session 并不一样

6年前 评论

@andyzu 我不是本文原作者,所以无法提供技术支持。不过,依照原文作者的看法,还是使用原始的 Session 比较合适,没必要 JWT。你也可以试试 Laravel Passport 的方式,下发 OAuth Access Token。

6年前 评论

@WilliamMa 是的,原文作者也想表达这个想法,场景不同,不应该把 JWT 作为 Session 机制。

6年前 评论

@史沟飞 我觉得可以在服务器用缓存把 jti 存储起来,验证的时候在比对一下 jti 就可以了

6年前 评论
LivisSnack

JWT 机制还是很危险的,通过抓包工具就可以轻易获取到认证 token 值。而有些 APP 的 token 值过期时间设置很长(例如某某打卡软件 -- 过期时长长达一年),就可以各种模拟请求了!

6年前 评论

@LivisSnack 这跟 JWT 无关。其它 Session 机制,只要是基于 HTTP 协议,或者说只要有通讯,都可以被抓出来。所以才需要 HTTPS~

6年前 评论
LivisSnack

@Wi1dcard https 也一样抓的!

6年前 评论

@LivisSnack 那是因为你在你的机器上信任了抓包工具提供的证书。这叫做 MitM「中间人攻击」,客户端不信任证书的话,是无法解密 HTTPS 流量的。

6年前 评论

@Flourishing 推荐给你一个 laravel5.8 支持 markdown 的开源博客 VienBlog

6年前 评论
耐小心

所以如果要做 API 不建议使用 JWT 而是使用 session? 然后使用 ip_hash 或者是 redis 来保证登录态的同步?

6年前 评论

@耐小心 API 为什么一定要牵扯到会话(所谓登录态)同步?

6年前 评论
lmaster

@Wi1dcard 哇,白俊遥是你同事,很早以前就关注了他的 tp-blog,貌似转 laravel 后就不更新了

6年前 评论
lmaster

事,不说不清;理,不辩不明。
我的高中英语老师一直强调,翻译要做到:信、达、雅。
其中,求其信,已大难矣。译文要准确,不偏离,不遗漏,也不要随意增减意思。
所以,本文是难得好的译文,同时也是好的论帖。

已赞,已收藏
最后声明,不论谁对谁错,本人对参与讨论者都表示尊敬。在学习的路上本就寂寞,若没有争执与讨论更加凄凉。

6年前 评论

嗯 还在尝试中 个人还是喜欢用 jwt 来做认证

6年前 评论

@lovecn 我的博客链接做过一次升级,之前是 https://wi1dcard.cn/分类名/文章简称/ 后来统一为 https://wi1dcard.cn/posts/文章简称。因此你可以将中间的分类名(例如 wtfdocumentstutorials 等)替换为 posts 即可访问。感谢关注!

6年前 评论

@lovecn 评论内的原链接已替换为新链接,可以直接访问啦。

6年前 评论
medz

个人一直使用 JWT 其实是因为 jwt 方便,我解决当个用户的 token 情况是,自定义编码用户内容,大多数人仅编码一个 user id 进去,我在用户表有一个 token_salt,其作用是用户修改密码这类操作的时候修改数据库的 salt,这样旧 token 达到无法验证通过的效果。强下限某一个用户的所有 token 也是修改盐值。有一个弊端,就是用户在客户端修改密码后,当前 token 不可用了,我的解决方法是用户修改密码后返回一个新的 jwt token 供后续使用。看如何使用了理解吧。原文我看了,说的是比较初阶的使用方法。jwt 的扩展性个人理解也提现在这里。可以通过很多方法进行处理。毕竟在自定义信息阶段仅使用已有工具包默认方式,确实很不安全。其实就是看怎么用吧。

5年前 评论
medz

我所谓的 jwt 方便,不要误解,而是在实现后交互上的方便。我的大多数开发场景不涉及到 web 端。oauth2 则不那么方便。而且我一直不用 oauth2 的原因是 b2c 场景下,即使是密码模式也需要 client id 这类信息,生成一条放在服务端,然后客户端传递用户账号密码来生成 token 有点脱了裤子放屁的感觉。oauth2 个人感觉适用 b2b 场景。

5年前 评论

个人一直使用 jwt, 其实不能说 jwt
个人理解的,cookie 保存 session_id, 然后后端通过 session_id 获取具体的 session 信息
我的 jwt 是,LocalStorage 保存一个随机字符串的 token, 然后后端通过 token 查找具体的信息
session 注销也只是让后端的 session 信息失效,并不能让 cookie 里面的 session_id 失效,只是 session_id 对应的信息失效了
那 token 也一样,token 还是有效的,只是 token 对应信息失效了
token 的适用场景更广泛,定制性更强,如果多个项目共享用户状态也更容易实现

5年前 评论

题目说的是 “别再使用 JWT 作为 Session 系统”,简单来说怎么保持登录状态。为什么要保持登录状态,是因为 HTTP 是短连接,登录之后连接已经断开无法知道下次连接上一次连接的关系,从而无法保持登录状态。我们为了保持连接使用 Cookie 机制,Cookie 其实简单来说是保存数据用的,我们可以把连接状态保存到数据里。由于 Cookie 可以从服务器读写(Cookie 机制浏览器实现),所以往往我们登录登出都是有服务器控制,简单来说客户端无感知,不需要前端做什么。这个机制一直运行比较好,直到出现了 APP 时代,我们的程序不仅仅在浏览器上实现,我们的程序也需要支持本身的 app 开发,但是由于 Cookie 是 W3C 的标准不是 HTTP 的标准,而 APP 本身不支持 Cookie 为了解决这个问题才出现了 Token 的机制,JWT 本身就是这样一个机制。Token 机制本身是灵活的,不依赖于 Cookie,他可以存在任何地方,内存中,LocalStorage,Cookie,文件等等,所以简单来说 Token 更灵活更广泛,而且它只是解决保持登录的一种机制而已,至少现在来看,它比较适合现在应用程序的开发。我觉得博主上面的比较有点太注重细节,你说的那些比较事实上都不是很么致命的问题,都是可以通过程序去避免的,这些比较都不能说使用 Token 这种机制有什么不好。

5年前 评论
Wi1dcard (楼主) 5年前

我想了解一下微信开放平台的授权验证有用到哪些协议 比如已知的 oauth2.0 授权 openid 还有其他的吗?

5年前 评论

认真看完了以后,觉得某些观点过于主观,我觉得 Token 和 Session ID 一样,提供一种承载身份认证的可能,需不需要无状态取决于是否需要撤销 Token,你想安全一点可以在后端用各种方式去让某个 Token 失效,Session 不也是这样么。

将 JWT 向 Session 一样使用是一种方案,比 Session 更灵活,它是渐进式的,可以无状态,也可以有状态,而 Session 是一步到位。

我相信绝大多数后端都是将角色和权限等与用户关联,而是不是 Token,所以是否真的存在数据延迟,还得看怎么用 JWT!换句话说如果用同样的方式(将用户角色与权限放到 Cookie 中)去使用 Cookie + Session 也存在同样的问题。

4年前 评论

好文章。。对区分 JWT,Session 更加清晰了。。感谢分享。。一直在纠结接口使用 session 还是 jwt 纠结。。

4年前 评论

我来说说我的看法吧

session 是以前 jsp 或者其他后端代码混合时代,服务端渲染时服务端用来检测会话用户的凭证吧,一般是 sessionId.

而现在前后端分离的模式,和多端兼容的需求,sessionId 的模式已经不大适用了,然后就有了 token 的一样个验证模式。

token 验证模式使用比较广泛的就是 jwt,当然你也可以自己写一个自己的 token 来判断,还有个情况就是把以前的 sessionId 作为 token,至于哪种好根据自己的业务去判断。

jwt 有优势是一个无状态,但安全性的话有些缺陷。

jwt 如果追求个绝对的安全,那肯定是要做成有状态的,可控的,这完全没问题的。

jwt 有无状态都可以,根据自己业务去处理,无可厚非。当然你就要用 sessionId 也行,符合自己的业务就行

4年前 评论

jwt 这玩意只是他封装了一个生成 token 的方法吧 我完全可以自己生成吧 可以简单这么理解吧 毕竟任何不基于服务器控制的 token 都不是好 token 他无非就是客户端去存 自己去解而已 但是实际运用都结合到 redis 吧 既然用到 redis 了 我不如自己生成 token 算啦

2年前 评论

未填写
文章
67
粉丝
592
喜欢
1236
收藏
1136
排名:13
访问:32.4 万
私信
所有博文
社区赞助商