解决Shiro jwt并发刷新token问题

2023-10-29

        使用shiro jwt做应用系统的权限校验,网上通用的方式是这样的。

在用户登录时候会生成两份token,一份AccessToken用于返回给前端,前端带上这个令牌去请求后台接口,通常过期时间较短5分钟左右,一份RefreshToken放在Redis中,两个Token的加密值都是当前时间戳,当用户的AccessToken过期时,就去从Redis中通过用户名取得Redis中的RefreshToken,比较AccessToken和RefreshToken中的时间戳是否一致,如果一致就重新生成一组AccessToken和RefreshToken,他们值都是当前时间戳。然后返回给前端。用户继续带着这个新AccessToken请求接口,原则上如果用户持续点击,就可以一致无限的刷新Token,但是这种方式如果页面都是单请求,自然没有问题,但是页面不可避免的都会有并发的请求。如果有并发请求过来,只有第一个请求可以正常返回,其他的后续所有请求都会失败,因为第一个请求已经刷新的Redis中的时间戳,返回给了前端的新的AccessToken,后续的并发请求自然带着老AccessToken就会全部报错。

        这种情况怎么解决呢?

        1. 保证前端请求的顺序执行,这种方式比较消耗前端性能接口请求较慢

        2. 在后端刷新RefreshToken写入Redis时加入Redis写入锁,只有最先最快拿到锁的线程允许修改Redis中的值,锁定时间设置为30s,30s后解锁,写入数据。

        3.缓存旧的AccessToken,每次需要刷新Token的时候都把老Token存储一份,可以放在Redis中,或者一个全局变量中,有效时间30s。也就是说30s内带着老Token请求依然有效

        这里主要详细描述下第三种方式如何实现。

//全局变量用于缓存旧的Token
 Map<String,Long> tempToken = new HashMap<>();
  /**
     * shiro验证成功调用
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        String jwttoken= (String) token.getPrincipal();
        if (jwttoken!=null){
            try{
                if(TokenUtil.verify(jwttoken)){
                    //判断Redis是否存在所对应的RefreshToken
                    String account = TokenUtil.getAccount(jwttoken);
                    Long currentTime=TokenUtil.getCurrentTime(jwttoken);
                    if (RedisUtil.hasKey(account)) {
                        Long currentTimeMillisRedis = (Long) RedisUtil.get(account);
                        if (currentTimeMillisRedis.equals(currentTime)) {
                            return true;
                        }
                    }
                }
                return false;
            }catch (Exception e){
                if (e instanceof TokenExpiredException){
                    System.out.println("AccessToken过期了");
                    if(tempToken.containsKey(jwttoken)){
                        // 判断缓存中是否有token,有的话判断时间是否小于30s,小于30 通过,大于30 清空map
                        Long currentTimeMillis =System.currentTimeMillis();
                        long r = (currentTimeMillis - tempToken.get(jwttoken))/1000;
                        if(r <= 30 ){
                            System.out.println("小于30 所以通过");
                            return true;
                        }else{
                            System.out.println("大于30 所以通过");
                            tempToken.clear();
                            return false;
                        }
                    }
                    if (refreshToken(request, response)) {
                        return true;
                    }else {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * 刷新AccessToken,进行判断RefreshToken是否过期,未过期就返回新的AccessToken且继续正常访问
     * @param request
     * @param response
     * @return
     */
    private boolean refreshToken(ServletRequest request, ServletResponse response) {
        String token = ((HttpServletRequest)request).getHeader("X-Auth-Token");
        String account = TokenUtil.getAccount(token);
        Long currentTime=TokenUtil.getCurrentTime(token);
        // 判断Redis中RefreshToken是否存在
        if (RedisUtil.hasKey(account)) {
            // Redis中RefreshToken还存在,获取RefreshToken的时间戳
            Long currentTimeMillisRedis = (Long) RedisUtil.get(account);
            // 获取当前AccessToken中的时间戳,与RefreshToken的时间戳对比,如果当前时间戳一致,进行AccessToken刷新
            if (currentTimeMillisRedis.equals(currentTime)) {
                // 获取当前最新时间戳
                Long currentTimeMillis =System.currentTimeMillis();
                tempToken.put(token,currentTimeMillis);
                RedisUtil.set(account, currentTimeMillis,
                        CommonData.REFRESH_EXPIRE_TIME);
                // 刷新AccessToken,设置时间戳为当前最新时间戳
                token = TokenUtil.sign(account, currentTimeMillis);
                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
                httpServletResponse.setHeader("Authorization", token);
                httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
                return true;
            }
        }
        return false;
    }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解决Shiro jwt并发刷新token问题 的相关文章

  • 如何使用 Apache POI API 将图像添加到 pptx 中添加的图像占位符?

    我已经预定义了带有文本和图像占位符的 pptx 模板 我如何从模板访问和修改这些占位符 我可以使用 POI pptx API 直接将图像和文本添加到幻灯片中 但如何将其添加到模板的占位符中 请参阅链接以了解如何添加占位符来创建固定模板 ht
  • Eclipse 在源代码管理中保存操作

    我们希望找到一种在签入之前执行代码标准的 轻量级 方法 我们真的很喜欢使用 Eclipse 内置的想法保存操作 go to Preferences gt gt Java gt gt Editor gt gt Save Actions 其中有
  • Hibernate注解放置问题

    我有一个我认为很简单的问题 我见过两种方式的例子 问题是 为什么我不能将注释放在字段上 让我举一个例子 Entity Table name widget public class Widget private Integer id Id G
  • Java:迭代 Collection 的最佳方法(此处为 ArrayList)

    今天 当我看到一段我已经使用了数百次的代码时 我很高兴地开始编码 迭代集合 此处为 ArrayList 出于某种原因 我实际上查看了 Eclipse 的自动完成选项 这让我想知道 在什么情况下以下循环比其他循环更好使用 经典的数组索引循环
  • 正则表达式拆分数字和字母组,不带空格

    如果我有一个像 11E12C108N 这样的字符串 它是字母组和数字组的串联 如何在中间没有分隔符空格字符的情况下分割它们 例如 我希望分割结果为 tokens 0 11 tokens 1 E tokens 2 12 tokens 3 C
  • 如何使用 Java 处理 Selenium WebDriver 中的新窗口?

    这是我的代码 driver findElement By id ImageButton5 click Thread sleep 3000 String winHandleBefore driver getWindowHandle drive
  • 使用 AES SecretKey 的 Java KeyStore setEntry()

    我目前正在 Java 中开发一个密钥处理类 特别是使用 KeyStore 我正在尝试使用 AES 实例生成 SecretKey 然后使用 setEntry 方法将其放入 KeyStore 中 我已经包含了代码的相关部分 The KS Obj
  • Java 中的“Lambdifying”scala 函数

    使用Java和Apache Spark 已用Scala重写 面对旧的API方法 org apache spark rdd JdbcRDD构造函数 其参数为 AbstractFunction1 abstract class AbstractF
  • 普罗米修斯指标 - 未找到

    我有 Spring Boot 应用程序 并且正在使用 vertx 我想监控服务和 jvm 为此我选择了 Prometheus 这是我的监控配置类 Configuration public class MonitoringConfig Bea
  • Javafx过滤表视图

    我正在尝试使用文本字段来过滤表视图 我想要一个文本字段 txtSearch 来搜索 nhs 号码 名字 姓氏 和 分类类别 我尝试过在线实施各种解决方案 但没有运气 我对这一切仍然很陌生 所以如果问得不好 我深表歉意 任何帮助将不胜感激 我
  • 如何知道抛出了哪个异常

    我正在对我们的代码库进行审查 有很多这样的陈述 try doSomething catch Exception e 但我想要一种方法来知道 doSomething 抛出了哪个异常 在 doSomething 的实现中没有 throw 语句
  • 如何在JSTL中调​​用java方法? [复制]

    这个问题在这里已经有答案了 这可能是重复的问题 我只想调用不是 getter 或 setter 方法的方法例如 xyz 类的 makeCall someObj stringvalue Java类 Class XYZ public Strin
  • 测试弱引用

    在 Java 中测试弱引用的正确方法是什么 我最初的想法是执行以下操作 public class WeakReferenceTest public class Target private String value public Targe
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用
  • javafx android 中的文本字段和组合框问题

    我在简单的 javafx android 应用程序中遇到问题 问题是我使用 gradle javafxmobile plugin 在 netbeans ide 中构建了非常简单的应用程序 其中包含一些文本字段和组合框 我在 android
  • Eclipse 中 Spring MVC 模型对象的 (jsp /jstl) 视图中的代码辅助

    在 Spring MVC 中 当将对象放置在视图模型中时 如下所示 public String getUser Model model fetch user model addAttribute user user return viewN
  • 具有特定参数的 Spring AOP 切入点

    我需要创建一个我觉得很难描述的方面 所以让我指出一下想法 com x y 包 或任何子包 中的任何方法 一个方法参数是接口 javax portlet PortletRequest 的实现 该方法中可能有更多参数 它们可以是任何顺序 我需要
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String
  • 如何从 Maven 存储库引用本机 DLL?

    如果 JAR 附带 Maven 存储库中的本机 DLL 我需要在 pom xml 中放入什么才能将该 DLL 放入打包中 更具体地举个例子Jacob http search maven org artifactdetails 7Cnet s
  • 调整添加的绘制组件的大小和奇怪的摆动行为

    这个问题困扰了我好几天 我正在制作一个特殊的绘画程序 我制作了一个 JPanel 并添加了使用 Paint 方法绘制的自定义 jComponent 问题是 每当我调整窗口大小时 所有添加的组件都会 消失 或者只是不绘制 因此我最终会得到一个

随机推荐

  • Python学习笔记(三十五)- 异常设计(Designing with Exceptions)

    1 这个问题是第1章第一次测验的重复内容 看 我告诉你这很容易 为什么 spam 出现在书籍和网络上的这么多Python例子中 困扰我很久的问题终于得到了解答 答 因为Python是以英国喜剧组织Monty Python的名字命名的 基于我
  • 第一节课笔记(环境部署)

    大挖掘 分析与应用展望 大数据指无法在可承受的时间范围内用常规软件工具进行捕捉 管理和处理的数据集合 是需要新处理模式才能具有更强的决策力 洞察发现力和流程优化能力的海量 高增长率和多样化的信息资产 数据挖掘 Data Mining 是有组
  • python远程连接ssh_Python实现SSH连接远程服务器

    首先需要安装paramiko模块 coding utf 8 author MuT6 Sch01aR import paramiko ssh paramiko SSHClient 创建SSH对象 ssh set missing host ke
  • STM32电源框图解析(VDD、VSS、VDDA、VSSA、VREF+、VREF-、VBAT等的区别)】

    VCC 电路的供电正电压 VDDD 芯片的工作数字正电压 GND 电路的供电负电压 VSSD 芯片的工作数字负电压 VDD 芯片的工作正电压 VREF ADC基准参考正电压 VSS 芯片的工作负电压 VREF ADC基准参考负电压 VDDA
  • setAccessible(true)安全检查不通过 Bean转Map

    setAccessible true 安全检查不通过 Bean转Map public static Map
  • 自己搭建IntelliJ IDEA授权服务器

    尊重原创 原文链接 感谢 首先说明 服务器文件不是我写的 我也是从lanyu大神那里搬过来的 我这里只是详细介绍一下如何使用 lanyu大神博客 http blog lanyus com archives 174 html 下载服务器文件
  • 数据库协议

    1 TDS协议 参考链接 https blog csdn net u014608280 article details 80776703 https blog csdn net cxzhq2002 article details 57203
  • [Err] 1064 - You have an error in your SQL syntax; checkthe manual that corresponds...解决方法总结与分析

    Err 1064 You have an error in your SQL syntax checkthe manual that corresponds to your MySQL serverversion for the right
  • windows命令行获取文件夹内所有文件列表

    命令 dir dir 获取帮助信息结果如下 显示目录中的文件和子目录列表 DIR drive path filename A attributes B C D L N O sortorder P Q R S T timefield W X
  • cocos creator 两个刚体的碰撞检测

    想做玩家和障碍物的碰撞阻拦 需要给玩家和障碍物添加刚体 但是添加之后还是会穿透 百度后发现玩家的移动不能通过设置坐标实现的 必须要设置刚体速度的方式来实现玩家移动 1 给玩家 障碍物添加刚体 会看到添加了RigidBody和PhysicsB
  • 启动sonarqube 报错 AccessDeniedException: /opt/module/sonar/sonarqube-7.5/temp/conf/es/elasticsearch.yml

    报错信息 WrapperSimpleApp Encountered an error running main java nio file AccessDeniedException opt module sonar sonarqube 7
  • vue的拖拽插件: vue.draggable

    中文文档地址 vue draggable中文文档 itxst comVue Draggable是一款基于Sortable js实现的vue拖拽插件 支持移动设备 拖拽和选择文本 智能滚动 可以在不同列表间拖拽 不依赖jQuery为基础 vu
  • 2023-2024最新python毕业设计选题推荐大全

    文章目录 0 前言 1 python 算法类 毕设选题 2 python 数据挖掘 毕设选题 3 python 大数据处理 云计算 区块链 毕设选题 4 python 网络安全 毕设选题 5 python 游戏设计 动画设计类 毕设选题 适
  • LeetCode 7. 整数反转

    题目链接 7 整数反转 注意题目已经更改为不允许存储 64 位整数 有符号或无符号 class Solution public int reverse int x int total 0 while x if total gt 0 tota
  • H5项目怎么打包成APP

    文章目录 前言 一 新建5 APP项目 二 删除不需要的文件 三 将H5打包的文件拷贝到当前目录下 四 配置APP 五 发行 云打包 六 安装apk 总结 前言 开发uni app的编辑器HBuilderX可以将H5项目打包成APP 相信很
  • 牛客SQL大厂面试真题目录

    1 某音短视频 SQL156 各个视频的平均完播率 SQL157 平均播放进度大于60 的视频类别 SQL158 每类视频进一个月的转发量 率 SQL159 每个创作者每月的涨粉率及截止当前的总粉丝量 SQL160 国庆期间每类视频点赞量和
  • etc的常见算法_ETC面试题总结.doc

    ETC面试题总结 ETC 面试 ETC 面试1 一 测试2 二 UNIX4 三 Oracle7 四 智能网方面10 五 C 方面11 六 网络13 七 操作系统16 八 数据结构17 九 其它19 测试 软件在开发过程中的测试流程 讲一下软
  • Sublime Text 3配置Go语言开发环境

    Sublime Text 3配置Go语言开发环境 1 Go语言环境搭建 2 GoSublime安装和配置 2 1 安装步骤 2 2 代码开发 2 3 编译运行 1 Go语言环境搭建 本篇博文是在读者Go自身环境已经搭好 Sublime Te
  • 华为OD机试 - 求最小步数(Java)

    题目描述 求从坐标零点到坐标点n的最小步数 一次只能沿横坐标轴向左或向右移动 2 或 3 注意 途径的坐标点可以为负数 输入描述 坐标点n 输出描述 输出从坐标零点移动到坐标点n的最小步数 备注 1 lt n lt 10 9 用例 输入 4
  • 解决Shiro jwt并发刷新token问题

    使用shiro jwt做应用系统的权限校验 网上通用的方式是这样的 在用户登录时候会生成两份token 一份AccessToken用于返回给前端 前端带上这个令牌去请求后台接口 通常过期时间较短5分钟左右 一份RefreshToken放在R