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

JWT 如何实现一个账号只能同时在一个设备(端)登录

  •  
  •   imherer · 2019-04-10 16:10:22 +08:00 · 11449 次点击
    这是一个创建于 2041 天前的主题,其中的信息可能已经有所发展或是发生改变。

    初次尝试 JWT,发现一个账号生成多个 JWT token 后,只要在这些 token 还没过期前都是有效的

    例如账号 A 在 15:30 分登录生成了 token0,有效时间为 1 小时,过了 5 分钟账号 A 再次登录生成了 token1 有效时间还是为 1 小时。 这时候 token0 和 token1 都是合法的? 我之前以为 token1 会把 token0 给刷新掉……


    那么如何实现一个账号只能同时在一个设备(端)登录呢?想到了如下几种方式:

    • 1.将 token 存入 redis,每次登录就刷新 redis 里的 token,这样就能保证 token 永远只有一个(虽然实际上还是有多个,但是我只认 redis 里的这一个)。但是这样的话我就不需要 JWT 了啊,直接 redis 实现就好了
    • 2.结合 session,每次登录将 session 写入 token,然后判断当前 session 和 token 里的是否一致。同上面,这样是不是有点冗余了,直接用 session 实现就好了?
    • 3.将用户最后一次登录信息写入 token,判断 token 里的登录信息和数据库里最后一次的登录信息是否一致。这样做的话会频繁查询数据库,数据库负载会变高

    各位大佬有什么好方法吗?

    19 条回复    2021-01-26 11:35:39 +08:00
    noe132
        1
    noe132  
       2019-04-10 16:13:55 +08:00   ❤️ 1
    直接用 session。
    whileFalse
        2
    whileFalse  
       2019-04-10 16:14:44 +08:00   ❤️ 2
    你需要 session。jwt 面向离线认证设计,其本就不准备支持吊销功能。

    session 放内存或者缓存服务,不会对数据库造成压力。
    aimerforreimu
        3
    aimerforreimu  
       2019-04-10 16:19:22 +08:00 via Android   ❤️ 1
    jwt 设计的时候就有专门的使用场景的,你这样需求的话,用 session 比较合适,物尽其用是最吼的,没有必要强制用 jwt 的
    hantsy
        4
    hantsy  
       2019-04-10 19:33:04 +08:00   ❤️ 2
    如果用 Spring, 可以使用 Spring Session ( 可以选择由 Redis 等存储), 除了实现传统 HTTP Session 接口外,,还是可以在应用之间共享,可以作为 API 安全方案,也适合于 microservice 架构.

    https://github.com/hantsy/spring-microservice-sample#secures-microservice


    如果自己实现,不管是什么语言,也可以尝试将 Token 写入 Redis,Redis 写入时可以可以设置 Timeout。
    xuanbg
        5
    xuanbg  
       2019-04-10 20:18:20 +08:00
    JWT 不支持的,自己撸一个吧。我的经验是把一些用户信息搞成一个对象放 Redis,生成的 Token 只包含令牌本身的 ID、用户 ID 和验证串就行了。这样服务端在验证 Token 的时候就很灵活了,可以轻松实现楼主的单设备登录需求。
    freakxx
        6
    freakxx  
       2019-04-10 23:56:23 +08:00
    如果是 drf 那套的话,
    login 的时候直接把 token 更新下就可以。


    jwt 的过期和刷新的理念,当时用的时候觉得不太顺手,git 上面好像还有一个 issue 就是讨论这个问题。
    kangzai50136
        7
    kangzai50136  
       2019-04-11 05:15:12 +08:00 via Android
    直接 spring session 吧,虽然我自己用 jwt+redis。我当初听说分布式的话 jwt+redis 比 session 好才选了 jwt。
    whileFalse
        8
    whileFalse  
       2019-04-11 08:30:16 +08:00
    @freakxx 过期和刷新概念的目的就是弥补 jwt 不支持服务端主动吊销的问题。
    如果业务不需要主动吊销,直接签发无限时间或 99 年的 jwt 即可。
    huangdayu
        9
    huangdayu  
       2019-04-11 09:28:02 +08:00
    存 jwt 的 id,对 id 进行操作,删除则是吊销
    zhenjiachen
        10
    zhenjiachen  
       2019-04-11 09:45:31 +08:00
    我是用户有一个 version 字段,jwt 里面存 username 和 version,把 username 和 version 存到 redis,每次登陆 version 加 1,然后清除 redis 缓存,上一个 token 来了判断 token 里面的 version 字段,不相等就返回 401
    alexmy
        11
    alexmy  
       2019-04-11 11:29:53 +08:00
    把 JWT 搞复杂了,就相当于自己实现了一遍 session。
    zy445566
        12
    zy445566  
       2019-04-11 11:53:51 +08:00
    简单 jwt 里面记录用户 id 和生成一个随机值,登陆的时候用户表存一下这个随机值。验证用户的时候拿用户 id 去查用户表看看这个随机值是不是就是 jwt 的随机值,如果不是,就作废当前 jwt 就好了
    jerray
        13
    jerray  
       2019-04-11 13:28:05 +08:00
    用 JWT 的话也是能做的,需要实现一套黑名单机制。首先对每个 JWT 生成唯一 ID,用 jti 放在 payload 里面。当你要给某个 Token 设置为无效时,就把这个 ID 放到黑名单里。因为你的 Token 是有过期时间的,所以仅需要对未过期的 Token 进行黑名单检查。

    缺点是需要额外的存储和查询。需要存储用户生成的每个 Token ID。因为有过期机制存在,可以定时对过期的 Token ID 记录进行清理来节省空间。
    reus
        14
    reus  
       2019-04-11 13:49:53 +08:00
    “只能同时在一个设备(端)登录”这种需求定期抽样检查就行了,那么严格干嘛,浪费资源。
    jswh
        15
    jswh  
       2019-04-11 15:13:56 +08:00
    当你涉及到需要服务端存储登录态的时候,其实就是 session 了。JWT 这种就是在客户端存储的登录态,只不过做了协议约束和加密。
    Alife8
        16
    Alife8  
       2019-06-04 11:04:01 +08:00
    我在寻找一种允许最多 x 个设备登录的最优方案.至于 1 个设备(帐号),似乎要好解决一些。想法和上面几位同学有相似!
    1.在用户表多加一个字段 R
    2.给 JWT 设定刷新时间
    3.给 JWT 数据上加入 R 字段
    当用户新登录时,给 R 一个随机值,并加入到 JWT 中返回给用户。
    JWT 要刷新时,判断 JWT.R 值和用户表.R 值是否相等,不同则要求用户重登录,相同则返回新的 JWT。
    当然这个 JWT 只有在刷新时才判断,若要解决这个问题可以加入一个黑名单缓存表,在用户新登录时根据最后登录时间判断上次生成的 JWT 刷新时间是否还未到。这样在用户请求数据时先查一下黑名单。这样还有一个好处,在用户修改了密码后之前的 JWT 就无效了.

    不足:不够优雅
    优点:似乎也只能这样了
    Alife8
        17
    Alife8  
       2019-06-04 11:12:11 +08:00
    刚说的多加一个 R 其实也没必要,直接用最后登录时间代替就好!
    PS:v2 上居然不能编辑!!
    0x1001
        18
    0x1001  
       2020-09-24 09:44:11 +08:00
    konglizhi3362
        19
    konglizhi3362  
       2021-01-26 11:35:39 +08:00
    登录的三种模型:
    多地登录:同一账号可以在任意地点同时登录,互不影响
    单地登录:在同一时间一个账号只能在一个地点登录,新登录会挤掉旧登录者
    同端互斥登录:在同一类型设备上只允许单地点登录,在不同类型设备上允许同时在线
    楼主需要的是第二种,单地登录,建议了解一下 sa-token 框架,这个框架有登录问题解决方案,一行代码解决你的问题
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1146 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:13 · PVG 02:13 · LAX 10:13 · JFK 13:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.