【shiro】问题记录--为什么refreshToken方法走不下去

一、前言

最近做Jwt token续签的时候,在很多博客和下载的代码中,都是在JWTFilter中进行token的刷新,于是就按照了网上的代码进行尝试,代码如下:

1. 代码

  1. 在JWTFilter中的isAccessAllowed方法
    目的:就是想通过executeLogin内部的方法,出现了token过期,然后返回TokenExpiredException的异常,就进行refreshToken
 @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        //判断请求的请求头是否带上 "Token"
        if (isLoginAttempt(request, response)){
        	// 省略一些其他操作,主要看以下核心内容
			try {
                 //如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确
                 executeLogin(request, response);
                 return true;
             }catch (Exception e){
                 /*
                  * 注意这里捕获的异常其实是在Realm抛出的,但是由于executeLogin()方法抛出的异常是从login()来的,
                  * login抛出的异常类型是AuthenticationException,所以要去获取它的子类异常才能获取到我们在Realm抛出的异常类型。
                  * */
                 Throwable cause = e.getCause();
                 if (cause!=null&&cause instanceof TokenExpiredException){
                     //AccessToken过期,尝试去刷新token
                     String result = refreshToken(request, response);
                     if (result.equals("success")) {
                         return true;
                     }
                 }
             }
		}
    	return false;
    }        
  1. 在JWTFilter中的refreshToken方法
    目的:刷新token,进行续签。
    PS:这里主要说问题,不做详细说明
	@Autowired
    @Lazy
    private RedisUtil redisUtil;
    
	private Boolean refreshToken(ServletRequest request,ServletResponse response) {
        HttpServletRequest req= (HttpServletRequest) request;
        // 获取传递过来的accessToken
        // 从请求头header中获取字段名为ACCESS_TOKEN的值(也就是我们说的token)
        String token = req.getHeader(CommonConstant.ACCESS_TOKEN);
        // redis中的token 定义前缀+token 为缓存中的key,得到对应的value(cacheToken)
        Object cacheToken = redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token);

        // 获取token里面的用户名
        String userName = JwtUtil.getUsername(token);

        // 判断refreshToken是否过期了,如果过期了,redis的key将不存在
        if (CommonUtils.isNotEmpty(cacheToken)){
			...
		}

2. 设置

使用的依赖:

		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.2.0</version>
		</dependency>

为了测试这部分正常使用,设置了token的签证过期时间为1分钟,redis中存储token的过期时间也为1分钟。

PS:看网上都是这样设置的。

二、问题

1. 问题一:e.getCause()获取不到TokenExpiredException

登录获取token后,过了1分钟,在访问接口,断点打在了Throwable cause = e.getCause();,查看捕获的异常为org.apache.shiro.authc.UnknownAccountException: Realm,如下:
在这里插入图片描述
翻译过来就是:找不到提交的AuthenticationToken的帐户数据。
想到token过期就1分钟,好像也对,redis存的过期,token也过期,但是为啥没有获取到TokenExpiredException这个异常?
TokenExpiredException:token过期而抛出的异常。

2. 问题二:refreshToken方法走不下去

  1. 为什么redisUtil为null
  2. 即使redisUtil不为null,但cacheToken也为null

在这里插入图片描述

三、查看源码

  • 从executeLogin往下看源码(问题二)
    这个的代码,主要是执行了Subject接口中的login,详细的可以看下==【shiro】subject.login(token)源码== 这篇文章,这里给出流程图:
    在这里插入图片描述基于以上文章的内容,快步查看源码。

根据 问题 中的截图,对着下面subject.login(token)的流程查看报错信息在哪输出,最后发现在ModularRealmAuthenticator类中。
在这里插入图片描述
该异常主要是因为info返回值为null,具体看下AuthenticationInfo info = realm.getAuthenticationInfo(token);
在这里插入图片描述
这个类大家应该都很熟悉了,看到doGetAuthenticationInfo就是我们在realm中重写的身份验证方法。
这个类中要得到info返回值为null,那就得看getCachedAuthenticationInfo(token)方法。
在这里插入图片描述
在该方法中,主要判断cachetoken是否为null,如果为null那info只能返回null。
PS:这个地方还是有点迷,debug进来会发现,redis即使没有过期,这里的cache还是为null,而且这地方为null,整个流程其实还是正常进行。(这里点以后遇到在继续研究)

四、总结

redis和token同时过期,以上代码就没法进行token续签了。

  1. 问题一
    主要原因是token过期,所以查不到信息,导致返回org.apache.shiro.authc.UnknownAccountException: Realm
    解决一:redis中token过期时间设为20分钟,token签证的过期时间设为1分钟,将两个时间错开可以执行,再去实验,就可以得到TokenExpiredException的异常了。(具体没再往下深究)
  2. 问题二
    2.1 原因:拦截器在bean初始化前执行的,这时候redisUtil是null,需要通过SpringUtils进行反射获取
    2.2 原因:token过期了(问题一中同样的问题)
    解决二:将两个时间错开

PS:全是个人理解,请大佬们指导一下👀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值