关于shiro session失效报错问题
2015-08-03 14:35
423 查看
最近做了一个项目,要用到shiro,做完之后发现有个异常经常发生org.apache.shiro.session.UnknownSessionException: There is no session with id ,经过多天的研究,终于得以解决
登录的时候异常信息:
为何找不到呢,原因是这样的。
当用户登录的时候,web容器tomcat或者jetty会在线程池里面启用线程调度,线程里面找到对应的servlet,shiro登录的时候代码如下
方法SecurityUtils.getSubject()源码是这样
我们清楚的看到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,代码如下
登录的时候异常信息:
org.apache.shiro.session.UnknownSessionException: There is no session with id [4e8fe40a-6347-4c53-b273-829889656f6e] at org.apache.shiro.session.mgt.eis.AbstractSessionDAO.readSession(AbstractSessionDAO.java:170)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSessionFromDataSource(DefaultSessionManager.java:236)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSession(DefaultSessionManager.java:222)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doGetSession(AbstractValidatingSessionManager.java:118)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupSession(AbstractNativeSessionManager.java:108)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupRequiredSession(AbstractNativeSessionManager.java:112)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.mgt.AbstractNativeSessionManager.getAttribute(AbstractNativeSessionManager.java:209)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.mgt.DelegatingSession.getAttribute(DelegatingSession.java:141)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.subject.support.DelegatingSubject.getRunAsPrincipalsStack(DelegatingSubject.java:469)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.subject.support.DelegatingSubject.getPrincipals(DelegatingSubject.java:153)[251:org.apache.shiro.core:1.2.3] at org.apache.shiro.subject.support.DelegatingSubject.getPrincipal(DelegatingSubject.java:149)[251:org.apache.shiro.core:1.2.3]
为何找不到呢,原因是这样的。
当用户登录的时候,web容器tomcat或者jetty会在线程池里面启用线程调度,线程里面找到对应的servlet,shiro登录的时候代码如下
<span style="white-space:pre"> </span>Subject currentUser = SecurityUtils.getSubject(); String sessionId = ""; if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5Hex(passwd)); token.setRememberMe(true); currentUser.login(token); }
方法SecurityUtils.getSubject()源码是这样
public static Subject getSubject() { Subject subject = ThreadContext.getSubject(); if (subject == null) { subject = (new Subject.Builder()).buildSubject(); ThreadContext.bind(subject); } return subject; }
我们清楚的看到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,代码如下
@Override public String login(String username, String passwd) { Subject currentUser = SecurityUtils.getSubject(); //提前1秒去判断 防止这个if没进 等执行下面的时候它却失效了 <span style="font-family: Arial, Helvetica, sans-serif;">lengthenTimeOut是失效时间</span> if((System.currentTimeMillis()-currentUser.getSession().getStartTimestamp().getTime())>=lengthenTimeOut-1000){ ThreadContext.remove(ThreadContext.SUBJECT_KEY);//移除线程里面的subject shiroSessionManager.getSessionDAO().delete(currentUser.getSession());//移除失效的session currentUser = SecurityUtils.getSubject();//重新获取subject } String sessionId = "";
try { if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5Hex(passwd)); token.setRememberMe(true); currentUser.login(token); } sessionId = currentUser.getSession().getId().toString(); } catch (ExcessiveAttemptsException ex) { log.info(username + "帐号被锁定1小时!", ex); } catch (UnknownAccountException uae) { log.info(username + "账户不存在!", uae); } catch (IncorrectCredentialsException ice) { log.info(username + "密码不正确!", ice); } catch (LockedAccountException lae) { log.info(username + "账户被禁了!", lae); } catch (AuthenticationException ae) { log.info(username + "用户名或密码错误!", ae); } catch (UnknownSessionException ue) { log.info("登录session失效" + sessionId, ue); } log.info("登录成功返回的sessionId+++++++++++++" + sessionId); return sessionId; }
相关文章推荐
- PHP底层工作原理
- 留几手:互联网创业到底是咋回事
- 线索二叉树模型的建立与遍历
- 电影节 SDUT 2797
- Help Me with the Game(POJ--2996
- VISIBLE INVISIBLE GONE
- ios自动布局
- My97DatePicker用法及下载
- python安装error: Unable to find vcvarsall.bat
- struts--token防止表单重复提交(源码分析)
- Qt的二维图形
- DevExpress ChartControl 设置它的标题
- shell脚本之间互相调用
- p3145 汉诺塔游戏
- acp,amv:command not found
- 13.Roman to Integer (罗马数字转成整数)
- Cent Savings (DP)
- 怎么将Word转换成PDF的方法分享
- poj 1860 Currency Exchange (最短路,Bellman-Ford算法)
- DataGridView 某一列单元格内容居中显示