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

jwt 如何做在线踢人功能?

  •  
  •   SkyLine7 · 2023-10-11 11:47:09 +08:00 · 9199 次点击
    这是一个创建于 441 天前的主题,其中的信息可能已经有所发展或是发生改变。

    不依赖第三方中间件(比如 redis )

    73 条回复    2023-10-12 17:31:07 +08:00
    bootvue
        1
    bootvue  
       2023-10-11 11:49:00 +08:00
    必须依赖中心化的存储机制
    Pythoner666666
        2
    Pythoner666666  
       2023-10-11 11:53:39 +08:00
    不配合第三方中间件做不到
    JoeDH
        3
    JoeDH  
       2023-10-11 11:54:02 +08:00
    token 存在哪,那就在哪把 token 删掉
    codehz
        4
    codehz  
       2023-10-11 11:54:46 +08:00
    还是需要一个地方存 revocation list ()
    相比于其他不透明 session 来说,revocation list 可以在后端用 bloom filter 做初筛(因为数量上 revoke 的肯定比较少,分发 bloom filter 的状态更便宜)
    wu00
        5
    wu00  
       2023-10-11 11:56:25 +08:00   ❤️ 1
    玩骚的是吧,一次性 token ,从签发处来控制
    BBCCBB
        6
    BBCCBB  
       2023-10-11 11:59:10 +08:00
    redis 记录这个 token 是否还能用. 白名单/黑名单都可以.
    nikenidage1
        7
    nikenidage1  
       2023-10-11 11:59:29 +08:00
    这就是 jwt 的缺点之一啊
    thinkershare
        8
    thinkershare  
       2023-10-11 12:00:46 +08:00   ❤️ 7
    Jwt 主要用来给资源授权的,生命周期很短。几乎不做权限回收控制。如果想要做有状态的会话凭证,又不想维护服务器状态,理论上就是矛盾的。
    luckyrayyy
        9
    luckyrayyy  
       2023-10-11 12:01:23 +08:00
    无状态怎么记录状态?开着车怎么走路?
    cp19890714
        10
    cp19890714  
       2023-10-11 12:10:28 +08:00
    办法:可以把 JWT 标签存储到服务端,用户访问时,校验当前状态是否有效,如果无效,就禁止访问。

    另外,需要踢人,就不应该用 JWT 。JWT 滥用,V 站都说了很多次了。
    sblid
        11
    sblid  
       2023-10-11 12:10:40 +08:00   ❤️ 7
    既然需要状态就不要用 jwt ,否则自己重新实现了一个 session ,不是没事找事么。
    nothingistrue
        12
    nothingistrue  
       2023-10-11 12:12:18 +08:00
    做不了。JWT 只是个 Token ,不要只用它来做会话跟踪。在线踢人属于会话跟踪,必须上服务器端会话管理。
    xiaogezz
        13
    xiaogezz  
       2023-10-11 12:19:24 +08:00
    JWT 只做好认证的事情就好了,至于具体的访问权限,应该交由权限控制
    ns09005264
        14
    ns09005264  
       2023-10-11 12:22:42 +08:00
    我有个想法,那就是做和 session 相反的事,即:把要踢的用户保存在内存里。
    具体是这样的:
    用户改了密码或者要踢某个用户下线,就把该用户的 ID 保存到内存里,因为要踢下线的人一般都是少数,费不了多少空间。
    当然要设置保存期限,和 JWT 的过期时间一样就行。
    比如用户改了密码,就把他的 ID 保存到内存里,当他依然用旧令牌访问时,后端从旧令牌中解析到他的 ID 并与内存中的列表比对,如果存在就令牌失效,需要登录,同时删除列表里的 ID 。
    如果他改了密码,但再也没有登录过刷新令牌,那么也无妨,因为内存中的列表过期也会清理的。
    简单来讲,因为 JWT 对后端服务是无状态的,但可以在后端增加“踢人列表”这一点点不怎么占用资源的状态。
    以上没有考虑分布式的情况,因为我没有分布式的经验。分布式的“登录与验证服务”应该也是单机的服务吧?
    Breacher
        15
    Breacher  
       2023-10-11 12:27:38 +08:00 via iPhone
    一直都是只把 JWT 当作 token 用,然后使用数据库跟踪它的状态:已退出、已 revoke 、重置密码后让旧 token 失效。我只是需要 JWT 的一些特性,比如能够携带一些键值对、能够验证签名(签名验证不通过直接返回未登录,避免将压力给到存储层)。
    streamrx
        16
    streamrx  
       2023-10-11 12:29:41 +08:00 via iPhone
    jwt 就是无状态
    NX2023
        17
    NX2023  
       2023-10-11 12:30:11 +08:00
    JWT 是没法撤销的,如果不依赖缓存,只能使用长短 JWT
    在线踢人是不行的,只能说把短 JWT 设置的短一些,比如 1min 或者 30s

    https://nickxu.me/post/jwt-refresh-token
    fordoo
        18
    fordoo  
       2023-10-11 12:32:46 +08:00
    提供一个思路,jwt payload 中保存一个用户的关键信息的签名,每次验证 jwt 的时候,同时去验证 db 里面用户的这个签名(可以加缓存减少 db 压力),需要踢 jwt 时,只需要让用户的关键信息变化就行
    hyperbin
        19
    hyperbin  
       2023-10-11 12:43:33 +08:00 via Android
    在用户表维护个 tokenStartTime ,鉴权时判断 jwt 的 iat 是否大于 tokenStartTime ,只要更新 tokenStartTime 到当前时间就可以拉黑以前签发的所有 token
    NX2023
        20
    NX2023  
       2023-10-11 12:47:27 +08:00
    @sblid 老哥,是这样的
    XCFOX
        21
    XCFOX  
       2023-10-11 12:50:30 +08:00
    @fordoo #18

    jwt 的出现就是为了无状态、不访问 db 。既然业务需求不能无状态,不如直接把 jwt 的签名、验证这一套省略了。用最简单的最直观的 session 模式。
    chendy
        22
    chendy  
       2023-10-11 13:09:02 +08:00
    直接不用 jwt ,jwt 本身就适合在服务间传递,减少用户中心这类服务的压力
    浏览器,客户端认证用普通的 token ( cookie 里的 sessionid 也算 token )
    PVXLL
        23
    PVXLL  
       2023-10-11 13:09:36 +08:00 via iPhone   ❤️ 1
    打电话给客户端使用者让他自己销毁
    zsdroid
        24
    zsdroid  
       2023-10-11 13:10:15 +08:00
    @ns09005264 负载均衡了解下
    aladdinding
        25
    aladdinding  
       2023-10-11 13:43:25 +08:00
    加一个黑名单的中间价
    mdn
        26
    mdn  
       2023-10-11 13:59:12 +08:00
    @fordoo 这样用户的其他端都会被踢掉,所有 token 失效,而不是单个
    hongfs
        27
    hongfs  
       2023-10-11 14:02:32 +08:00
    十月份才过去几天,第几个 JWT 帖子了。。。
    manasheep
        28
    manasheep  
       2023-10-11 14:04:53 +08:00
    token 时限设短,只有高危操作去验证用户账户状态,别的默认提供服务就可以了
    hez2010
        29
    hez2010  
       2023-10-11 14:06:40 +08:00
    其实最好的方法是不用 jwt
    lidashuang
        30
    lidashuang  
       2023-10-11 14:07:22 +08:00
    黑名单, 数据库存储黑名单
    fire2y
        31
    fire2y  
       2023-10-11 14:08:32 +08:00
    换个思路 把权限给禁掉就好了
    cslive
        32
    cslive  
       2023-10-11 14:52:38 +08:00
    数据库总用的吧,数据库中拉黑
    leegoo
        33
    leegoo  
       2023-10-11 15:00:03 +08:00
    因为 jwt 就是一个 token ,token 反解析出来可以获取到用户信息

    数据结构类似于 : key:token ,value:info

    我提供的思路是

    在获取到 token 后,再使用另外一个 map 存储,这个 map 的 key 是 用户账号(保证用户的唯一性即可),value 对应这个 token
    这样就能够保证 ,知道哪些用户登录了,如果要踢人, 只需要根据 新定义的那个 map 找到 对应的 token (可能有多个), 从 map 或者 redis 清理掉即可
    chenduke
        34
    chenduke  
       2023-10-11 15:10:13 +08:00
    1.缩小 token 的过期时间,10 分钟,5 分钟。3 分钟, 看具体的场景的需要。
    2.token 中包含生成的时间, 刷新 token 的时候与数据库中的最新刷新时间做比较,如果小于则返回需要重新登录。
    3. 上面二条并不能保证及时性, 有条件加个消息推送的机制,这样就很及时了。
    version
        35
    version  
       2023-10-11 15:20:55 +08:00
    @hongfs 生活压力大..最近太多人转行程序员了..只能找一些 10 年未维护的老旧项目接手(面试的人也不懂技术)...产品就会把遗留问题丢给新手..
    fengpan567
        36
    fengpan567  
       2023-10-11 15:23:01 +08:00
    把用户禁了
    fregie
        37
    fregie  
       2023-10-11 15:49:40 +08:00
    这个需求跟 jwt 存在的意义互斥
    swulling
        38
    swulling  
       2023-10-11 15:53:04 +08:00 via iPhone
    一个令人费解的设计就是明明服务请求都落中心数据库,别人建议用 redis 做个 session ,却说 jwt 适合分布式,没有单点。很奇怪。
    sunmlight
        39
    sunmlight  
       2023-10-11 15:56:53 +08:00
    判断 用户+签发时间, 早于“踢”的失效?
    Tiny
        40
    Tiny  
       2023-10-11 16:04:26 +08:00
    把 signing key 删了,token 失效
    fanchenio
        41
    fanchenio  
       2023-10-11 16:11:07 +08:00   ❤️ 4
    你是否在搜索 session ?
    zhiyu1998
        42
    zhiyu1998  
       2023-10-11 16:53:30 +08:00
    satoken
    pannanxu
        43
    pannanxu  
       2023-10-11 17:07:15 +08:00
    可以实现一个伪需求:使用 socket 推送,前端收到消息后执行踢出逻辑。这样需求可以实现,但如果手动保留 token 还是可以登录。
    summerLast
        44
    summerLast  
       2023-10-11 17:36:56 +08:00
    后台部署一个用户请求 401 (代价是所有用户的请求的 token 都做了验证)
    前台 401 清除 token
    fuchish112
        45
    fuchish112  
       2023-10-11 17:38:44 +08:00
    不要用 jwt
    bill110100
        46
    bill110100  
       2023-10-11 17:46:28 +08:00
    @ns09005264 不管是记录白名单还是黑名单,都需要一个记录的地方。对于分布式系统,还是需要每次使用前去记录的地方查询对比,和 session 方案没本质区别。
    IvanLi127
        47
    IvanLi127  
       2023-10-11 17:47:56 +08:00
    等过期就行了,access token 最多也就活几分钟。不想等就别作死用 jwt 。或者 jwt payload 里加个 version ,给所有人签发下一 version 的 token ,服务端直接把不是最新 version 的 token 拒绝掉。
    wunonglin
        48
    wunonglin  
       2023-10-11 17:48:44 +08:00   ❤️ 1
    典型的 JWT 滥用例子
    Dlin
        49
    Dlin  
       2023-10-11 17:54:26 +08:00
    简单,先这样,再这样,然后那样
    GTim
        50
    GTim  
       2023-10-11 19:57:11 +08:00
    楼上都没把数据说明白。JWT 是可以先获取到用户数据,然后从数据库里抽出用户数据再验证的,所以,最简单的方案,就是生成的时候加一个用户的 salt 进去,如果要踢出用户,只要改变这个用户的 salt 即可
    matrix1010
        51
    matrix1010  
       2023-10-11 22:19:06 +08:00
    假设踢人是低频操作。 有数据库,签发 token 可控的情况下感觉可以这样: 把用户分为 n 组, 每组存个 version 到数据库里,签发 jwt 的时候把用户组的 version 也带上。踢用户时用户组对应的 version+1 。服务器每分钟同步整个用户组/version 表到内存(或者用 mq 实时性更高),解析 jwt 时对比内存里的 version 和 token 里的 version ,不匹配就走数据库查询用户有没有被踢。比如 1000 万用户分 1000 组,最坏情况就是这一组的 10000 个用户同时请求
    xuanbg
        52
    xuanbg  
       2023-10-11 22:40:39 +08:00
    JWT 做不到有状态。所以,正确的做法是放弃 JWT ,自己实现一个有状态的 token ,非常简单。
    iwdmb
        53
    iwdmb  
       2023-10-12 00:29:05 +08:00
    配合数据库
    gitrebase
        54
    gitrebase  
       2023-10-12 04:23:48 +08:00
    op 这个需求有点矛盾,主要在与“在线踢人”是一个 stateful 的操作,而 JWT 最大的卖点就是 stateless ;可以考虑放弃 jwt 使用 session ;如果真要做的话就只能用一些存储中间件吧,想不到别的比较好的方案了
    byaiu
        55
    byaiu  
       2023-10-12 07:48:05 +08:00
    建议看这个帖子

    https://v2ex.com/t/979326
    kwanzaa
        56
    kwanzaa  
       2023-10-12 08:22:36 +08:00
    灰名单,查一下又不会死。
    ionfev
        57
    ionfev  
       2023-10-12 09:11:17 +08:00
    JWT 是个字符串,是服务端使用密钥单向哈希计算出来的。按理说前端用后端给的 Token 字符串请求,服务端只验证,不保存状态,适合多个后端的分布式。但是呢,后端从来不信任前端(虽然说 Token 是后端生成的,所有的接口后端都要验证判断),所有一般还是后端使用 Redis 保存 Token 的有效期,所以对于踢人功能来说,删除后端 Redis 里的 Token 就行。
    expy
        58
    expy  
       2023-10-12 09:27:32 +08:00   ❤️ 4
    服务端加上状态管理,然后就成了自己发明的半吊子 session 。
    justfindu
        59
    justfindu  
       2023-10-12 09:38:41 +08:00
    每个用户本地文件系统存一个私钥, 踢人就删私钥, 无法解码就失效. 下次登录创建新私钥保存. 私钥名可以使用 userId_hash(token), 删除时候查找 userId_* , 你看是不是可行.
    jonsmith
        60
    jonsmith  
       2023-10-12 09:48:42 +08:00
    Jwt 有效期尽量短一点,比如 30 分钟、1 小时,使用 refresh_token 刷新 Jwt token 时,再处理踢人逻辑。
    本质上,Jwt 授权签发后不能直接取消了,只能等过期重新签发。
    8355
        61
    8355  
       2023-10-12 09:49:05 +08:00
    你们的 jwt 不校验的吗????
    震惊了。。。
    bk201
        62
    bk201  
       2023-10-12 09:57:01 +08:00
    定时换密钥,但是会把人全踢了。
    nobject
        63
    nobject  
       2023-10-12 09:57:22 +08:00
    个人觉得加个黑名单就简单,要踢人,或者后台删除一个用户,用户修改重置密码等,都可以把这个用户加入到黑名单中。依靠 jwt 本身的功能,只能有效期设置短点吧
    neptuno
        64
    neptuno  
       2023-10-12 10:20:31 +08:00
    加个 jwt 黑名单
    Torpedo
        65
    Torpedo  
       2023-10-12 10:36:51 +08:00
    核心就是有没有查库呗。把你的 jwt 校验增加查库的这一步应该就可以了
    tairan2006
        66
    tairan2006  
       2023-10-12 10:45:14 +08:00
    我工作这么多年就没用过 jwt…
    pengxiaoyu
        67
    pengxiaoyu  
       2023-10-12 11:14:30 +08:00
    我的理解是 jwt 解析->userid+roleid 等信息,那这个用户是否是黑名单用户,是由用户服务告诉我的。拦截器里查询用户服务告诉我是否要把他踢掉。而用户服务是否去查表还是查 Redis ,和网关无关,网关只做了从 jwt 解析这一步。
    mxT52CRuqR6o5
        68
    mxT52CRuqR6o5  
       2023-10-12 11:15:46 +08:00   ❤️ 2
    恭喜你,用一种很别扭的方式重新实现了 session
    coala
        69
    coala  
       2023-10-12 12:30:31 +08:00
    退而求其次,

    当前时间之前的 这个用户的签发 jwt 全部拉黑, gateway 加个逻辑判断就好了吧 。

    直接把这个用户所有的 token 的全干掉。
    BeautifulSoap
        70
    BeautifulSoap  
       2023-10-12 12:41:14 +08:00 via Android   ❤️ 1
    jwt 无状态,本质和互联网初期时,网站在 cookie 里塞用户 id 服务器靠 cookie 里记录的用户 id 来确认你是哪个用户没区别。只不过 jwt 更现代而且往往有效期非常短
    想要在服务器端实现 jwt 无效化,最后搞到最后你会发现这就是 session
    SuperXRay
        71
    SuperXRay  
       2023-10-12 13:53:37 +08:00
    不摄入食物的话怎么吃饱(不依赖注射营养物质)
    lmq2582609
        72
    lmq2582609  
       2023-10-12 17:11:31 +08:00
    jwt 就是个字符串,不依赖 redis ,那只能用程序的内存来代替 redis 了,需要持久化的话得自己实现。
    shea
        73
    shea  
       2023-10-12 17:31:07 +08:00
    可以做个拉黑功能, 在验证 jwt 前面加个中间件验证如果在 redis 里面存在的 token 就直接返回 401 就可以了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5370 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 08:38 · PVG 16:38 · LAX 00:38 · JFK 03:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.