jwt 如何做在线踢人功能? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
SkyLine7
V2EX    Java

jwt 如何做在线踢人功能?

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

    不依赖第三方中间件(比如 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 是否还能用. 白名单/黑名单都可以.
    liuliuliuliu
        7
    liuliuliuliu  
    PRO
       2023-10-11 11:59:29 +08:00
    这就是 jwt 的缺点之一啊
    thinkershare
        8
    thinkershare  
       2023-10-11 12:00:46 +08:00   8
    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 就是无状态
    nickxudotme
        17
    nickxudotme  
       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
    nickxudotme
        20
    nickxudotme  
       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
    G2bN4dbX9J3ncp0r
        30
    G2bN4dbX9J3ncp0r  
       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 清理掉即可
    f2A2RUpR2HgfHg5a
        34
    f2A2RUpR2HgfHg5a  
       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 拒绝掉。
    shakaraka
        48
    shakaraka  
    PRO
       2023-10-11 17:48:44 +08:00   1
    典型的 JWT 滥用例子
    Dlin
        49
    Dlin  
       2023-10-11 17:54:26 +08:00
    简单,先这样,再这样,然后那样
    yemoluo
        50
    yemoluo  
       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
    不摄入食物的话怎么吃饱(不依赖注射营养物质)
    dongdong12345
        72
    dongdong12345  
       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     Solana     2182 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 38ms UTC 00:42 PVG 08:42 LAX 17:42 JFK 20:42
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86