行业网站设计开发费用,网站可以在外地备案吗,wordpress影视主题模板免费下载,网站建设找 三尾狐问题描述#xff1a;
当用户通过登陆后进入一个web网站#xff0c;会把token保存到localStorage。假设token过期时间30min。
那么当用户在网站快乐地玩耍了30min后#xff0c;这时进行了一次提交表单#xff0c;它会被重定向到登陆页面。
作为用户#xff1a;我表单填了…问题描述
当用户通过登陆后进入一个web网站会把token保存到localStorage。假设token过期时间30min。
那么当用户在网站快乐地玩耍了30min后这时进行了一次提交表单它会被重定向到登陆页面。
作为用户我表单填了这么久点击提交时让我重新登陆我的体验很不好。 jwt的好处和局限
jwt的好处是服务器无需存储这些登陆的状态
而它的局限是服务器无法主动地通知用户“你的token过期了我重新给你一个”。 如何解决
方案一双token
在登陆时我们会生成俩个token
token表示正常情况下登陆凭证refresh-token表示需要刷新情况下登陆凭证
假设前者token)设置过期时间为30min后者为1天。
流程
time0min用户成功登陆后端返回俩个tokentoken和refresh-token)前端把它俩保存到localStoragetime10min用户进行了一次查询前端将俩个token都发给后端后端检验token有效返回结果用户很开心time35min用户提交了表单前端还是将俩个token发给后端后端检验token过期检验refresh-token有效后端认为这是要刷新token生成新的token和refresh-token成功返回数据和双token。前端把数据渲染把双token保存。
token正常过期的情况
当然token可以正常过期如果在检验时refresh-token也过期那就说明正常过期
假设time0min时用户登陆time2天时用户提交了表单那么后端认为这是正常过期用户需要重新登陆。
流程图如下 token刷新并发问题
现在很多登陆是可以多端的当多端并发都去尝试刷新token时会导致token被重复刷新
方案二刷新时用分布式锁
可以引入redis缓存中间件使用缓存加速并处理并发刷新问题。
将双token生成后存入redis。当需要刷新token时采用double-check获取关于refresh-token的分布式锁来实现。
伪代码如下 // 验证和刷新Token的方法 public String validateAndRefreshToken(String userId, String accessToken, String refreshToken) { String storedAccessToken redisTemplate.opsForValue().get(ACCESS_TOKEN_PREFIX userId); String storedRefreshToken redisTemplate.opsForValue().get(REFRESH_TOKEN_PREFIX userId); // 检查Access Token是否有效 if (storedAccessToken ! null storedAccessToken.equals(accessToken)) { return accessToken; // 直接返回原始的Access Token } // Access Token无效检查Refresh Token if (storedRefreshToken ! null storedRefreshToken.equals(refreshToken)) { // 使用双层检查和分布式锁控制高并发刷新 String lockKey refresh_lock: userId; boolean lockAcquired redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 5, TimeUnit.SECONDS); if (lockAcquired) { try { // 再次双层检查避免重复刷新 storedAccessToken redisTemplate.opsForValue().get(ACCESS_TOKEN_PREFIX userId); if (storedAccessToken null) { // 生成新的Access Token String newAccessToken generateToken(); redisTemplate.opsForValue().set(ACCESS_TOKEN_PREFIX userId, newAccessToken, 15, TimeUnit.MINUTES); return newAccessToken; } else { return storedAccessToken; // 直接返回已刷新过的Token } } finally { redisTemplate.delete(lockKey); // 释放锁 } } else { // 未获取到锁等待其他线程刷新完成再次获取已刷新的Access Token return redisTemplate.opsForValue().get(ACCESS_TOKEN_PREFIX userId); } } // 若Refresh Token也无效返回null或抛出异常需重新登录 throw new TokenInvalidException(Access and Refresh Token both expired.); } 方案三过渡时间没看懂