JWT 弊端解决思路(主动过期\权限更新)

探讨JWT(JSON Web Token)在无状态认证中的局限性及解决方案,包括白名单、黑名单管理和短期AccessToken策略,以及如何解决权限实时更新等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JWT弊端解决思路(主动过期\权限更新)

了解JWT(JSON Web Token):
  • 优点
    • 服务器开销小,使用 Session + Cookie 需要服务器缓存用户数据,而使用 JWT 则是直接将用户数据下发给客户端,每次请求附带一并发送给服务器.
    • 扩展性好。服务器不缓存用户数据的好处是可以很方便的进行横向扩容
    • 更符合RESTFul API,遵循无状态原则,而JWT刚好把鉴权放在了客户端.
  • 缺点
    • 发下去的令牌服务器端无法控制,在退出或更改权限后,不会立即失效.令牌泄露后不安全
    • 增加带宽成本,使用JWT后,所有请求都需要携带Token,而token的体积很大,如果token过大,加解密过长,也会影响请求速度

(本篇就是围绕这两个缺点准备的解决思路)

安全问题:

JWT保存在客户端,由于JWT无状态去中心化的特点,首先要通过服务端保存一定的信息控制令牌随时失效保证安全,
(如果服务器端保存了令牌的状态,又和JWT的无状态化\去中心化相矛盾)

一、白名单

将所有发下去的令牌全都登记在白名单中

  • 缺点:数量比较大,记录了所有下发的token,而且又像session了
  • 优点:可以管理所有的令牌状态,还可以放入权限信息
  • 使用思路:白名单可以同时将用户的权限放入Redis中,在做令牌验证的时候再把用户的权限放进去
    (但是如果用户的权限发生变更,则之前的令牌中的权限信息就变的不准确)
二、黑名单

只将用户退出登录,或者手动失效的令牌记录下来

  • 缺点:无法管理用户申请的所有令牌,像修改密码后,无法使所有令牌失效,并且权限信息必须放在令牌内
  • 优点:退出登录这种的操作会很少,所以不会像白名单维护的数据量那么大
  • 解决办法:黑名单不能控制所有下发下去的令牌.当用户想要强制失效所有令牌,可以保存一个用户的信息和时间节点,黑名单验证比对中同时增加判断此用户该时间点前的令牌全部失效
三、较短的AccessToken

设置较短的AccessToken时间不做管理,记录所有refreshToken,即只保证refreshToken的安全

​ 比如将AccessToken的失效时间设置为3分钟,当AccessToken只剩下一分钟的有效期,就去调一遍刷新令牌的接口,拿着有效的refreshToken来获取新的AccessToken

  • 缺点:无法保证令牌实时失效,而且会大量的使用refreshToken获取新令牌.
  • 优点:这种AccessToken会更符合JWT令牌的初衷!

可以缩短AccessToken的有效时间到一个可接受的范围,如果只允许用户一个端登录,并且加上前端的配合(修改密码或权限后,自动获取新token,浏览器删除原token).也是一个比较合理的方案

(该方案就需要根据实际情况来权衡利弊)


JWT长度问题:

使用JWT时,如果要把用户的权限信息放入,则令牌会很长,加解密时间也会影响效率:

  • 在JWT中只存放一些少量的用户信息
  • 将权限信息放在网关中,当令牌到达时,使用令牌里的用户信息(或者权限标识)换取权限信息

(如果作为微服务之间的调用很多,或许可以将服务与服务之间的调用省去鉴权部分,直接调用,提高请求的处理时间)


用户权限信息的实时刷新问题:

如果将权限信息\或者权限标识信息放在JWT中,在用户权限发生改变时,下发的令牌权限并不会更新:

  1. 只能在权限改变时,失效所有令牌重新使用refreshToken获取新的令牌
  2. 或者将每个用户的权限信息保存在白名单中,修改权限就修改白名单

※配合强大的缓存服务:

为了实现权限即时更新,多端登录,修改密码,踢人下线等功能.我们永远躲不开有状态.服务端永远都得把控着所有的登录信息

  • 在大型的分布式系统中就要配合着强大的缓存实现:使用白名单,将所有登录系统的用户,全部放到缓存中,存入用户鉴权的所有信息.
  • 这样对缓存服务的要求会很高,也就是说:
    • 缓存服务的容量->系统可容纳的同时在线的用户数量
    • 缓存的查询速度->系统的访问速度

最后发现,我们使用JWT,大费周章的弥补了它无状态带来的问题,最后只是为了使用它的两个特点(优点):1.分布式2.可以存放少量的用户信息 …
其实这些工作使用cookie+分布式缓存也是可以实现的

然而我们不使用传统的session,就是因为在分布式系统的环境下,使用session还需要做多机器服务之间的数据共享和同步,显然不占优势,所以说才有了JWT或者分布式缓存这些东西

结语: 没有最好的解决方案,只有最适合的方案,一切都要根据实际需求来灵活运用

(仅个人理解,求指教)

### 如何修改JWT令牌的过期时间设置 在实际开发中,可以通过多种方式调整JWT令牌的过期时间。以下是几种常见的方法: #### 方法一:通过配置文件动态管理过期时间 如果当前项目使用的是静态配置文件(如`application.yml`或`application.properties`),可以直接在此类文件中定义过期时间参数[^2]。例如,在Spring Boot项目中,可以这样配置: ```yaml jwt: expiration-time: 3600000 # 过期时间为3600秒(1小时) ``` 随后在代码逻辑中读取该值并用于生成JWT令牌。 #### 方法二:基于Redis动态控制过期时间 为了更灵活地管理JWT的有效期限,推荐采用Redis作为缓存工具来存储Token及其对应的过期时间。这种方式允许开发者随时更改某个特定用户的Token有效期而无需重启服务。下面是一个简单的实现思路: - **保存Token到Redis** 当创建一个新的JWT时,除了将其返回给客户端外,还需同步记录至Redis数据库,并指定其TTL(Time To Live),即存活周期。 ```java String token = generateJwtToken(user); // 假设这是生成JWT的方法 long expireMillis = getExpireTimeFromConfig(); // 获取配置中的超时时长(毫秒) // 将Token写入Redis并设定生存时间 redisTemplate.opsForValue().set(token, user.getId(), Duration.ofMillis(expireMillis)); ``` - **验证Token有效性** 每次接收到请求携带的JWT时,先查询Redis确认是否存在对应键名;若不存在,则表明此Token已过期或者从未注册成功。 #### 方法三:手动构建Payload自定义exp属性 另一种直接的方式是在编码阶段主动JWT Payload加入名为`exp`(Expiration Time)的时间戳字段[^3]。这个数值代表Unix纪元后的绝对时刻数(单位为秒)。一旦到达预设时限之后再尝试解码就会触发异常提示“Token has expired”。 示例Python脚本如下所示: ```python import time import jwt def create_jwt_token(secret_key, username): payload = { 'sub': username, 'iat': int(time.time()), # Issued At Claim 'exp': int(time.time()) + 86400 # 设置一天后到期 } encoded_jwt = jwt.encode(payload, secret_key, algorithm='HS256') return encoded_jwt.decode('utf-8') if isinstance(encoded_jwt, bytes) else encoded_jwt ``` 以上三种途径各有千秋,请依据具体业务需求选取最合适的方案实施部署。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值