V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JasonLaw
V2EX  ›  程序员

JWT 可以实现 stateless authentication,但是 refresh token 不是无状态的,那 JWT 的意义是什么?

  •  
  •   JasonLaw · 2021-03-02 16:52:20 +08:00 · 5484 次点击
    这是一个创建于 1366 天前的主题,其中的信息可能已经有所发展或是发生改变。

    相关资料:

    我的疑问:

    如果使用 JWT 实现 stateless authentication 的话,那么肯定会使用到 refresh token,也就是会存储 refresh token 和 user 的对应关系。那么为什么还要使用 JWT 呢?为什么不直接使用opaque token呢?

    额外阅读:

    Authentication as a Microservice - YouTube

    第 1 条附言  ·  2021-03-07 15:39:29 +08:00
    第 2 条附言  ·  2021-05-04 18:19:03 +08:00
    49 条回复    2021-03-03 13:26:53 +08:00
    rrfeng
        1
    rrfeng  
       2021-03-02 17:05:17 +08:00
    refresh token 为啥要存 user 对应关系? user id 在 token 里,解出来用就行了。
    baiyi
        3
    baiyi  
       2021-03-02 17:19:23 +08:00
    因为 refresh token 不是 JWT 的内容,而是 OAuth 的格式,只是有些框架 or 包使用 JWT 来传递 OAuth 的内容罢了。

    从 JWT 的格式来看,一旦授权发出令牌,那么就不能在过期之前对令牌的权限做些什么,一旦想做什么,比如说服务端注销,那么就一定会有状态。所以 JWT 只适合特定场景。

    我以前也问过类似的问题,可以看下
    dqzcwxb
        4
    dqzcwxb  
       2021-03-02 17:20:40 +08:00   ❤️ 7
    吹捧新技术概念追赶潮流,然后发现还是需要对用户进行管理比如踢下线封号等等,然后强行又做回 session 那一套
    Solace202
        5
    Solace202  
       2021-03-02 17:24:39 +08:00
    特定场景使用吧,可以维护个 token 黑名单来实现 token 过期,但是感觉不太美好
    qwerthhusn
        6
    qwerthhusn  
       2021-03-02 17:26:51 +08:00
    本来用的 JWT,后来有一处登录和登出的功能。
    然后就填一个黑名单到 redis 里面去,但是这样每次认证都要去查一次 redis

    那为啥不直接用 uuid 做 token,然后直接去查一次 redis 获取用户信息结束了?
    JasonLaw
        7
    JasonLaw  
    OP
       2021-03-02 17:31:02 +08:00
    @baiyi #3 你的那个问题是 https://www.v2ex.com/t/381996 吧?我把它移过来,方便大家查看。
    LeeReamond
        8
    LeeReamond  
       2021-03-02 18:04:30 +08:00 via Android
    @qwerthhusn 请问什么场景要实现登出功能,一般不是前端删了 cookie 就完事了。如果前端没有按预期删除,那是前端非法使用,感觉也不会造成什么问题吧
    IvanLi127
        9
    IvanLi127  
       2021-03-02 18:08:27 +08:00
    jwt 在业务中,不用查库就能知道用户基本信息,只要占点 cpu 校验下。我觉得这个就是他的价值。
    IvanLi127
        10
    IvanLi127  
       2021-03-02 18:10:26 +08:00
    @LeeReamond 前端其实算是用户侧的东西了,前端非法使用可以代表用户非法使用,这锅前端不背。
    LeeReamond
        11
    LeeReamond  
       2021-03-02 18:13:54 +08:00 via Android
    @IvanLi127 当然是指用户非法。。前端这也能杠。。我的意思是什么业务场景要做登出黑名单?
    siweipancc
        12
    siweipancc  
       2021-03-02 18:25:30 +08:00 via iPhone
    实际上大部分场景就是当 session,为了实现前后分离 与客户端在线限制
    DOLLOR
        13
    DOLLOR  
       2021-03-02 18:26:04 +08:00   ❤️ 1
    @LeeReamond

    当 token 被盗取了,用户紧急下线,添加至黑名单(直到过期),防止被盗取的 token 继续生效。
    LeeReamond
        14
    LeeReamond  
       2021-03-02 18:28:48 +08:00 via Android
    @DOLLOR 这个就比较魔幻了,jwt 本身是 httponly 的,除非用户开 f12 控制台,把 jwt 抄下来发给别人。。。我感觉这种场景并不多见
    freakxx
        15
    freakxx  
       2021-03-02 18:33:38 +08:00
    @JasonLaw #2

    重签的时候,直接打开 refresh token 判断是不是过期,然后拿着 key 再签出一个的 access token 。

    某种程序上说,access token 和 refresh token 的最大区别在于时间长短,并且用短时间的 access token 来做请求。
    freakxx
        16
    freakxx  
       2021-03-02 18:36:42 +08:00
    @LeeReamond #14

    从 token 角度说,存在泄漏的问题,token 的信息存在于服务端的话,那么重新建一个就 ok 。
    但 jwt 没办法做到,因为 auth 只是判断 jwt 有没过期, 解出的 auth key 是不是对的。

    所以需要有一个机制来控制 token 的生命周期。
    IvanLi127
        17
    IvanLi127  
       2021-03-02 18:37:51 +08:00 via Android
    @LeeReamond #11 没杠你,我想表达的是前端的行为无法控制,在删了之前 token 都可能泄露。登出是为了让用户能撤销这个授权。非法使用得后端程序知道了才行,不然在后端眼里,他不知道这 token 非法了。
    freakxx
        18
    freakxx  
       2021-03-02 18:41:31 +08:00
    @LeeReamond #14

    换句话说,jwt 是基于时间来控制 token 的有效,在签出的时候已经决定有效期。

    另外 2 个可以的场景,sso 情况下,

    1.
    修改密码后,那么旧的签出 token 必须失效,这时候必须有一个地方来记录 某用户时间点以前的 token 全部不能使用;

    2.
    只允许一个设备( token )登录,那么每次拿到新的 access 后,必须禁用这个时间点前的 token ;
    uptonking
        19
    uptonking  
       2021-03-02 18:51:32 +08:00 via iPhone
    @IvanLi127 相比 redis 查内存中的数据有优势吗
    no1xsyzy
        20
    no1xsyzy  
       2021-03-02 19:16:50 +08:00
    @LeeReamond 场景不多见问题是影响大
    R 小 L 大的场景仍然是需要防御的。
    LeeReamond
        21
    LeeReamond  
       2021-03-02 19:21:21 +08:00 via Android
    @freakxx 像我上面说的,httponly 的东西,其实不太清楚怎么才能泄露,如果不会泄露的话,更改密码后旧 cookie 感觉不禁用也无所谓。

    同一时间只能单平台登录这个倒是确实有问题,这个场景也许天生 jwt
    LeeReamond
        22
    LeeReamond  
       2021-03-02 19:27:57 +08:00 via Android
    @LeeReamond 也许天生不适合 jwt
    KuroNekoFan
        23
    KuroNekoFan  
       2021-03-02 19:38:52 +08:00
    感觉 jwt 的价值在于提供了一种机制验证两端的敏感信息是可信任的吧
    IvanLi127
        24
    IvanLi127  
       2021-03-02 20:20:19 +08:00 via Android
    @uptonking #19 省空间,集群或者多服务下不用想着怎么共享数据。
    johnsona
        25
    johnsona  
       2021-03-02 20:26:06 +08:00
    面试用的,你懂什么,不说一句 jwt 都不好意思见人
    Kaciras
        26
    Kaciras  
       2021-03-02 20:56:25 +08:00
    连设备一起泄露不就行了。

    用户手机被盗,登陆过保存了 JWT,重置密码后让原先登录失效,这 JWT 就做不了。
    iConnect
        27
    iConnect  
       2021-03-02 21:08:09 +08:00 via Android
    @LeeReamond 用户账号重要的话,必须实时踢下线,否则账号被盗、意外泄露密码这些情况,就只能干瞪眼了。
    dorothyREN
        28
    dorothyREN  
       2021-03-02 21:27:05 +08:00
    用数据库存储的一会密码做 jwt 的 key,这样修改过密码以后 token 就自然失效了
    Smash
        29
    Smash  
       2021-03-02 21:34:37 +08:00
    @Kaciras #26 可以做到的,把原先的 JWT 加入到黑名单即可。
    Kaciras
        30
    Kaciras  
       2021-03-02 22:33:23 +08:00
    @Smash 黑名单总得保存到哪里,这就不是 stateless 的了
    VHacker1989
        31
    VHacker1989  
       2021-03-02 23:18:48 +08:00
    都回答到点子上,jwt 里面有个 expire 字段,是根据这个来判断是否过期的
    lyqqqq
        32
    lyqqqq  
       2021-03-02 23:35:29 +08:00 via iPhone
    一些项目一开始用 jwt,也只判断过期时间。架不住后面提了新需求需要踢下线,干不过产品。不想重新改认证。只能做个黑名单。
    leeg810312
        33
    leeg810312  
       2021-03-02 23:47:23 +08:00 via Android   ❤️ 1
    jwt 有很多特性,也许最开始是包含无状态的设计意图,但在我看来这不是它最有用的特性,无状态不符合安全需求完全可以忽略这个特性。

    我觉得 jwt 最有价值的特性:可以跨多个域名验证,Web 服务器能以任意网络拓扑结构横向扩展而不影响校验,客户端可以多种方式保存(浏览器 cookie 、HTML 存储对象、APP 用户数据文件等)或发送( URL 参数、form 表单等)。你当然可以自搞一套来满足这些需求,但 jwt 这样非常细节具体的一个标准很容易进行行业标准化实现,所以网上各种开发平台不同公司和开发者实现的 jwt 类库和产品都是相容的,可以开箱即用,节省大量开发成本。
    sampeng
        34
    sampeng  
       2021-03-03 01:03:45 +08:00 via iPhone
    jwt 和 refres token 是两回事…用 jwt 可以不用 refresh token 啊。用 refresh token 也可以不用 jwt…
    nvkou
        35
    nvkou  
       2021-03-03 01:52:11 +08:00
    其实可以参考 sso 接入的做法。
    jwt 只是替代了密码来验证身份而已。使用 refresh token 只是协调最新身份信息,达到类似 session 的效果。而对于服务端来说业务可以不理会 jwt,自己管理 session (仅验证身份)。那么在需要强制下线的场景下服务端可以先使自身管理的 session 失效,再通过业务向自营 sso 后台管理用户(全局下线或者锁定账户)。
    普通场景下服务端可以根据业务设置合理的 session 时间,自营 sso 则授权较长的时间。在用户眼里就是请求的时候有可能会有一瞬间的重定向到 sso 获取当前服务端的 session,此场景也适用于横向扩展。

    我们是用的 keycloak 作为 sso 服务,同时也是其他 sso 的 broker 。 保证自己有绝对控制权和权限分配。另外其实 SAML 协议的功能更完善些
    nvkou
        36
    nvkou  
       2021-03-03 02:01:53 +08:00   ❤️ 1
    我之前也想过这个问题。jwt 的有效性靠数字签名保障而已,无篡改是 ok 的,但信息的时效性却很难保证,毕竟钥匙已经发出去了。虽然用户信息是集中在 sso 服务器,改密码等操作自然可以 void 掉当前所有的授权。但也有可能服务端会有需求终止授权或权限变更。那么一个隐秘的 service account 用于从服务端操作 sso 服务器就很有必要了。 如果完全信任社会化登录( qq,微信,微博,google,apple )就无法满足要求。既然用户的关键数据需要自行管理(权限,黑名单等)。到不如自己也做个 sso 中转一下,反正也是 K-V 而已
    codehz
        37
    codehz  
       2021-03-03 02:10:00 +08:00   ❤️ 1
    @qwerthhusn #6
    (黑名单比白名单好的一点是:你可以中央服务器分发黑名单的布隆过滤器参数,这种过滤刚好适合于黑名单,因为不可能漏,只能将有效的错误检测为无效的,此时才需要发请求确认,可以大大降低查询的机会,而 push 模式也使得每个服务器需要保持的状态很少,更便于同步。。。(当然,可能会出现延迟,但是你可以让登记黑名单的步骤等待同步完成后才返回,就可以避免状态不一致)
    falcon05
        38
    falcon05  
       2021-03-03 03:25:40 +08:00 via iPad
    用上 refresh token 就变成 statefull 的了,跟传统的 session 相差不大,我看了它的实现,是在服务端使用表保存不同 app 的 token,方便直接销毁 token,这样可以做到有些场景,比如用户设备丢失,手动移除该设备的登录状态
    LeeReamond
        39
    LeeReamond  
       2021-03-03 05:13:59 +08:00
    @codehz 非常合理,感觉也许是这个问题的最终解答了
    micean
        40
    micean  
       2021-03-03 08:27:10 +08:00 via Android
    jwt 的主要场景是 api 授权
    frankly123
        41
    frankly123  
       2021-03-03 09:11:36 +08:00
    @qwerthhusn 做到后面你会发现还是有状态的
    CoreJa
        42
    CoreJa  
       2021-03-03 09:36:14 +08:00
    看业务需求吧,jwt/stateless token 的好处在于分布式场景,refresh token 纯属后来发现登出的场景不得不用到。但是 access token/refresh token 还是有实际意义的,至少使用 access token 的请求不需要走内存 /redis,而用 session 的话你每个请求都得查 session 。对于高并发场景的意义还是比较大的。
    leeg810312
        43
    leeg810312  
       2021-03-03 09:44:50 +08:00 via Android   ❤️ 1
    我觉得好多人因为 jwt 有无状态这个特性很纠结,好像非用这个特性不可,而忽略其他重要的特性。如果你深入看 jwt,就能理解 jwt 的无状态只是一个附带的特性,不是主要特性,是可以做成服务端无状态,而不是必须做成无状态
    hj24
        44
    hj24  
       2021-03-03 09:45:43 +08:00 via iPhone
    @Solace202 这样不还是有状态的了吗
    wolfie
        45
    wolfie  
       2021-03-03 09:49:26 +08:00
    那种带 refresh token 的,当 session id 用。也有服务端不存储的。
    Rocketer
        46
    Rocketer  
       2021-03-03 10:16:45 +08:00 via iPhone   ❤️ 1
    这种基于 Token 的验证方式,其实与证书的用法完全一致。

    证书发出去以后,所有客户端都可以离线自行验证,只要证书没过期,就认为是有效的,这对绝大多数情况足够了。

    同时 CA 那里还有个吊销列表,对于要求较高的客户端,可以定期更新吊销列表,并在证书的有效性验证中增加一步,检查证书是否在吊销列表中。

    Token 的目的是离线验证,即使有吊销列表,你也不能实时使用最新的吊销列表来查验,否则不如直接做成有状态的了。

    无状态是一种妥协,降低部分安全性,提高集群性能。它并不适合所有场景,不要硬往有状态的特性上面靠。
    dcoder
        47
    dcoder  
       2021-03-03 13:20:36 +08:00
    看到很多人都在抱怨 JWT, 我就放心了
    JWT 嘛其实天生就是残的...
    sozengtao
        48
    sozengtao  
       2021-03-03 13:26:36 +08:00
    其实这个问题简单 。只需要在 Redis 里面重新校验一次 Valid 就好了 。Redis 也可以手动过期
    jeffwcx
        49
    jeffwcx  
       2021-03-03 13:26:53 +08:00
    如果要主动刷新 token,其实可以不用 jwt
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1088 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:43 · PVG 03:43 · LAX 11:43 · JFK 14:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.