关于shiro session失效报错问题

最近做了一个项目,要用到shiro,做完之后发现有个异常经常发生org.apache.shiro.session.UnknownSessionException: There is no session with id ,经过多天的研究,终于得以解决


登录的时候异常信息:

[java]  view plain  copy
  1. org.apache.shiro.session.UnknownSessionException: There is no session with id [4e8fe40a-6347-4c53-b273-829889656f6e]  
  2.     at org.apache.shiro.session.mgt.eis.AbstractSessionDAO.readSession(AbstractSessionDAO.java:170)[251:org.apache.shiro.core:1.2.3]  
  3.     at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSessionFromDataSource(DefaultSessionManager.java:236)[251:org.apache.shiro.core:1.2.3]  
  4.     at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSession(DefaultSessionManager.java:222)[251:org.apache.shiro.core:1.2.3]  
  5.     at org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doGetSession(AbstractValidatingSessionManager.java:118)[251:org.apache.shiro.core:1.2.3]  
  6.     at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupSession(AbstractNativeSessionManager.java:108)[251:org.apache.shiro.core:1.2.3]  
  7.     at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupRequiredSession(AbstractNativeSessionManager.java:112)[251:org.apache.shiro.core:1.2.3]  
  8.     at org.apache.shiro.session.mgt.AbstractNativeSessionManager.getAttribute(AbstractNativeSessionManager.java:209)[251:org.apache.shiro.core:1.2.3]  
  9.     at org.apache.shiro.session.mgt.DelegatingSession.getAttribute(DelegatingSession.java:141)[251:org.apache.shiro.core:1.2.3]  
  10.     at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)[251:org.apache.shiro.core:1.2.3]  
  11.     at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)[251:org.apache.shiro.core:1.2.3]  
  12.     at org.apache.shiro.subject.support.DelegatingSubject.getRunAsPrincipalsStack(DelegatingSubject.java:469)[251:org.apache.shiro.core:1.2.3]  
  13.     at org.apache.shiro.subject.support.DelegatingSubject.getPrincipals(DelegatingSubject.java:153)[251:org.apache.shiro.core:1.2.3]  
  14.     at org.apache.shiro.subject.support.DelegatingSubject.getPrincipal(DelegatingSubject.java:149)[251:org.apache.shiro.core:1.2.3]  

为何找不到呢,原因是这样的。

当用户登录的时候,web容器tomcat或者jetty会在线程池里面启用线程调度,线程里面找到对应的servlet,shiro登录的时候代码如下

[java]  view plain  copy
  1. <span style="white-space:pre">    </span>Subject currentUser = SecurityUtils.getSubject();  
  2.           
  3.         String sessionId = "";  
  4.   
  5.         if (!currentUser.isAuthenticated()) {  
  6.               UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5Hex(passwd));  
  7.               token.setRememberMe(true);  
  8.               currentUser.login(token);  
  9.          }  


方法SecurityUtils.getSubject()源码是这样

[java]  view plain  copy
  1. public static Subject getSubject() {  
  2.         Subject subject = ThreadContext.getSubject();  
  3.         if (subject == null) {  
  4.             subject = (new Subject.Builder()).buildSubject();  
  5.             ThreadContext.bind(subject);  
  6.         }  
  7.         return subject;  
  8. }  


我们清楚的看到subject是从ThreadContext获取,创建过就直接从里面获取,没有创建的话就重新创建一个subject,然后绑定到ThreadContext,调用subject获取session的时候,他会去创建一个session,并且把session缓存起来,操作方法是在AbstractSessionDAO类里面,跟踪得知这里放的是subject的代理对象。如果session超时时间设置过短的话,在用户登录的时候,随着web容器分配的线程,很大的机会会分配之前的线程,而之前的线程绑定过了subject,subject没有失效,subejct对象里面的session也没有什么问题,但是session缓存里面的session失效了,用户登录的时候执行到currentuser.login(token)这个方法,他拿着之前的session,那后要去缓存里面读取,但是已经失效了,所以会报上面那个异常。


问题就是出现在这里,subject绑定到thread上下文里面,subject对象的session是个代理对象,正真的session是放在缓存里面,web容器随机分配的线程有可能绑定过subject,一旦session失效,就会报错。


解决的办法是在shiro去读取session之前判断有没有失效,如果失效移除ThreadContext里面的subject,并且删除缓存里面的session,代码如下

[java]  view plain  copy
  1. @Override  
  2.     public String login(String username, String passwd) {  
  3.           
  4.         Subject currentUser = SecurityUtils.getSubject();  
  5.         //提前1秒去判断   防止这个if没进  等执行下面的时候它却失效了  <span style="font-family: Arial, Helvetica, sans-serif;">lengthenTimeOut是失效时间</span>  
  6.   
  7.         if((System.currentTimeMillis()-currentUser.getSession().getStartTimestamp().getTime())>=lengthenTimeOut-1000){  
  8.             ThreadContext.remove(ThreadContext.SUBJECT_KEY);//移除线程里面的subject  
  9.             shiroSessionManager.getSessionDAO().delete(currentUser.getSession());//移除失效的session  
  10.             currentUser = SecurityUtils.getSubject();//重新获取subject  
  11.         }  
  12.           
  13.         String sessionId = "";  
[java]  view plain  copy
  1.     try {  
  2.         if (!currentUser.isAuthenticated()) {  
  3.             UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5Hex(passwd));  
  4.             token.setRememberMe(true);  
  5.             currentUser.login(token);  
  6.         }  
  7.          
  8.         sessionId = currentUser.getSession().getId().toString();  
  9.           
  10.   
  11.     } catch (ExcessiveAttemptsException ex) {  
  12.         log.info(username + "帐号被锁定1小时!", ex);  
  13.     } catch (UnknownAccountException uae) {  
  14.         log.info(username + "账户不存在!", uae);  
  15.     } catch (IncorrectCredentialsException ice) {  
  16.         log.info(username + "密码不正确!", ice);  
  17.     } catch (LockedAccountException lae) {  
  18.         log.info(username + "账户被禁了!", lae);  
  19.     } catch (AuthenticationException ae) {  
  20.         log.info(username + "用户名或密码错误!", ae);  
  21.     } catch (UnknownSessionException ue) {  
  22.         log.info("登录session失效" + sessionId, ue);  
  23.           
  24.     }  
  25.   
  26.     log.info("登录成功返回的sessionId+++++++++++++" + sessionId);  
  27.     return sessionId;  
  28. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值