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

观帖“登录最佳实践”有感, jwt token 如何优雅的刷新?

  •  
  •   AllenHua · 7 天前 · 2151 次点击

    原帖点我,一般的业务系统里 jwt token 都是如何刷新的?有没有最佳实践。

    亮出一些观点

    • jwt token 没有撤销机制,一旦泄露或者被窃取有安全隐患,所以 token 有效期也不建议设置的太长
    • 每次请求均生成新的 token 返回给前端有比较大的开销,太粗暴了
    • 利用 redis 验证,但是这样的话 jwt token 的 verify 功能就没有充分利用到,为什么还要用 jwt token?
    • 借鉴 oauth2 的设计使用 refresh_token ,对于小的业务系统应该是更加复杂了

    在 SpringBoot + redis + jwt token 的技术实践中,我的一点想法:

    1. 充分利用 jwt token 和 redis ,在拦截器中校验用户请求是否有效使用 jwt token 的 verify 和 redis hasKey 联合校验
    2. 设置一个 jwt token 已过期的缓冲区,比如设计 token 有效期 1 小时,但是在超时的 20 分钟内用户可以拿现有 token 换一个新的 token ,实现 token 的刷新。如果用户在此期间一直有操作系统,那么就不用重新登录。如果用户有 80 分钟没有使用系统就需要重新登录

    为的是服务器开销和业务需求之间寻求平衡。


    网上搜到一些讨论(如下),欢迎大家畅所欲言

    29 条回复    2021-11-28 09:56:18 +08:00
    psnnf
        1
    psnnf  
       7 天前
    如果要用 redis ,那么刷新 token 和不刷新 token 没有太大意义。
    如果不用 redis ,那么就是前端 cookie 倒计时,计算 token 到期时间,快到期请求一下刷新 token 的 api
    corningsun
        2
    corningsun  
       7 天前
    后端提供 refresh_token 接口,返回新的 token ;
    前端没有用户操作就不调用,过期就没了。
    前端用户一直操作的情况下,在 token 有效期内刷新一次就好了。
    gengchun
        3
    gengchun  
       7 天前
    没有必要做成过期还要缓冲吧?

    80 分钟无打操作重新登录,直接设置 X + 80 分钟的过期时间,超过 X 分钟就取一个新的 token 就好了。想减少拉 token 的次数,把 X 延长就好了。
    812603834
        4
    812603834  
       7 天前
    每个公司的 token 机制都不一样,像我们公司要求
    ①必须设置过期时间,我们设置的 30 分钟
    ②当操作频繁时,不能强行过期,就像用户在录入商品信息好不容易填写完了,提交..你给过期了,那会气死
    所以保存 token 的同时,保存过期时间。每次请求验证 token 时,校验过期时间,如果剩余时间不足 10 分钟
    那么就给 token 延长过期时间,延长 30 分钟,当期间不再有操作就自动过期。
    chendy
        5
    chendy  
       7 天前
    对于登录这种场景,按照一定业务规则生成 token 做 sesison id 就行了,用 JWT 发挥不出太大优势
    然后就是 刷新、续订 这些标准操作了
    xuanbg
        6
    xuanbg  
       7 天前   ❤️ 2
    token 无状态,发出去就管不了。要管理 token ,就得有状态,还是 session id 一样的东西,何必要用 JWT 。自己把 session id 和防伪码 base64 编码一下做 token 不更简单?
    AllenHua
        7
    AllenHua  
    OP
       7 天前
    @gengchun #3 这感觉和设置“缓冲时间”有些类似,假如 X == 40 ,那么每 40 分钟都会取一个新 token ,120 分钟未操作就让用户重新登录
    @corningsun #2 嗯嗯 这还是什么时候前端去刷新的问题,后端肯定会提供 refresh_token 的接口
    @psnnf #1 我看有些文章用了 redis 存储 token 但是没有用 redis 验证 token ,所以是存了个寂寞?
    xuanbg
        8
    xuanbg  
       7 天前
    JWT 虽好,但适用的场景非常有限,不要滥用 JWT 。
    psnnf
        9
    psnnf  
       7 天前
    @AllenHua 绝大多数公司用 JWT 后,然后有一些需求做不到,只能用到 redis 了,比如用户强行下线,一个用户只能一台设备登录到系统。。。等待这些需求,JWT 做不了
    AllenHua
        10
    AllenHua  
    OP
       7 天前
    @psnnf #9 是的。比如用户强行下线,redis delete key 然后 jwt 已签发的 token 无法撤回但是由于拦截器拦截请求校验的是 jwt verify 和 redis hasKey ,两者均返回 true 才可以访问资源,所以这样 redis 也能充分利用到了
    @812603834 #4 token 默认的载荷 payload 中就有 exp 字段吧,是个时间戳,我现在是声明了一个东八区给前端,这样在时间上前后端就没有误会

    > 如果剩余时间不足 10 分钟
    那么就给 token 延长过期时间,延长 30 分钟,当期间不再有操作就自动过期。

    是这样的。感谢分享 @812603834
    swulling
        11
    swulling  
       7 天前
    我的结论就是别用 JWT ,用中心化 Session ,比如 Redis ~
    gengchun
        12
    gengchun  
       7 天前
    @AllenHua 这样就是少了一个变量,少一次判断。另外第三方库可以直接实现。

    个人觉得,放了 redis 其实是为了实现流控,计费,或者蜜罐这些功能的话。

    而且一旦有 redis 的话,可以通过 redis 来实现对用户的管控。那样在不考虑合规因素的影响下,也没有必要把 jwt 的过期时间设置的太短,一般以天或者周过期 token 就足够了。
    mxT52CRuqR6o5
        13
    mxT52CRuqR6o5  
       7 天前
    通过存储到数据库给 jwt 添加 session 的特性,不就是另外又发明了一套 session 吗
    chendy
        14
    chendy  
       7 天前
    @xuanbg +1
    JWT 其实更适合服务之间用,网关鉴权后生成然后给后续服务传递着用,服务可以拿到可信的用户身份不需要再请求用户服务,请求处理结束就扔掉不用担心踢人之类的事情
    IvanLi127
        16
    IvanLi127  
       7 天前
    我觉得加 refresh_token ,这个 token 就是普通的 sessionID ,或者是一个 jwt , 但用户信息从数据库里取。前端定时更新 access_token 就好了。
    zzl22100048
        17
    zzl22100048  
       7 天前
    OIDC 不好用么
    joesonw
        18
    joesonw  
       7 天前
    上 redis 还要 jwt 干嘛? 只是 session 是正查, jwt revocation 是反查. 每次请求最终还是要去到 redis 查.
    AllenHua
        19
    AllenHua  
    OP
       7 天前
    @swulling #11 我了解一下
    @gengchun #12 网页应用我看 qq 邮箱之类的都是一两个小时没有操作会让用户重新登录的吧,我是觉得一周时间太长(说到底还是要看具体业务)
    @aboat365 #15 谢谢分享🙏🏻
    @zzl22100048 #17 我了解一下
    @joesonw #18 🤨
    zhleonix
        20
    zhleonix  
       7 天前   ❤️ 2
    JWT 可以有 JTI 唯一 ID ,需要支持注销的话广播一下 JTI 到各个访问控制点作为黑名单就行了。平时不用每次访问都去 redis 或者数据库验证,使用 JWT 自带的签名校验。
    hhyyd
        21
    hhyyd  
       7 天前   ❤️ 2
    我做了两个项目的登录功能后,目前的方案是:accessToken 过期时间设置短些,如果过期则前端用 refreshToken 获取新 token 即可(对用户来说是无感知的替换了 accessToken )。jwt 和 redis 选择一种方案即可,jwt 本身有附带信息解密即可,如果用 redis 关联 token-用户信息查询也很快。

    两种方案都有尝试过,相对更倾向加上 redis 去做记录,如果想要在服务端清除用户本次的登录状态,如果有 redis 记录就方便些;如果用的是纯 jwt 的话,不是很好办,最终还是要服务端记录这个 jwt 。

    楼主的过期超时 20 分钟的缓冲,其实就是 refreshToken 方式, 刚开始接触的时候,也用过这种 token 替换 token 的方式,后来发现还有 refreshToken 这种方式,非常适合这种频繁操作时延长 token 有效期的场景
    AllenHua
        22
    AllenHua  
    OP
       7 天前 via iPhone
    @hhyyd #21 很有用,谢谢分享
    xxfye
        23
    xxfye  
       7 天前 via Android
    每次请求都用中间件判断 jwt ,快过期了就发一个新的,附加到 header 上。拦截器发现新 token 的就替换旧的。
    nl101531
        24
    nl101531  
       6 天前 via iPhone
    设定过期时间,服务端检测每次请求时,token 还有多久过期,在一定允许时间范围内,重新下发。 这个方案需要 cookie 承接 token
    gengchun
        25
    gengchun  
       6 天前
    @hhyyd refesh token 总感觉有些多余。一次要整两个 token ,一个 refresh 和一个 access 。

    而目的好像只是防止第三方的 js 脚本可以读取 access token ,但是读不了 refresh token 。这个设计还有别的目的吗?
    AllenHua
        26
    AllenHua  
    OP
       6 天前
    @zhleonix #20 长见识了
    @gengchun #25 我原本也是这样认为的,但是新的认知还得重新建立
    chtcrack
        27
    chtcrack  
       5 天前
    从来不用 jwt,都是自己设计一套 token 系统..
    kssss
        28
    kssss  
       4 天前
    刷新啥,让用户退出重新登录
    AllenHua
        29
    AllenHua  
    OP
       4 天前 via iPhone
    @kssss #28 给你补充个 doge
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1867 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 00:23 · PVG 08:23 · LAX 16:23 · JFK 19:23
    ♥ Do have faith in what you're doing.