经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
Shiro使用Redis作存储之后更新Session失败的问题
来源:cnblogs  作者:Narcissu5  时间:2018/11/10 1:17:57  对本文有异议

问题

因为想在多个应用之间共享用户的登录态,因此实现了自己的SessionDAO,使用Kryo把SimpleSession序列化然后放到redis之中去,同时也使用了shiro.userNativeSessionManager: true来使用shiro自己的存储。然而之后一直出现丢失更新的问题,例如

  1. Session session = SecurityUtils.getSubject().getSession();
  2. User user = (User) session.getAttribute(MembershipConst.SessionKey.USER);
  3. user.setName("newName"); // 名称没有更新

分析

DEBUG之后发现,从Subject中取到的Session并不是我们在SessionDAO中创建的SimpleSession,而是DelegatingSubject$StoppingAwareProxiedSession,这是一个代理类,本身并不做任何事情,而是通过DelegatingSession调用真正的方法。而DelegatingSession实则也并没有真正的调用SimpleSession,而是调用的SessionManager中的方法:

  1. /**
  2. * @see Session#setAttribute(Object key, Object value)
  3. */
  4. public void setAttribute(Object attributeKey, Object value) throws InvalidSessionException {
  5. if (value == null) {
  6. removeAttribute(attributeKey);
  7. } else {
  8. sessionManager.setAttribute(this.key, attributeKey, value);
  9. }
  10. }

而默认的DefaultSessionManager在进行任何写操作之前总是会先通过SessionDAO读一次,如setAttribute方法

  1. public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
  2. if (value == null) {
  3. removeAttribute(sessionKey, attributeKey);
  4. } else {
  5. Session s = lookupRequiredSession(sessionKey);
  6. s.setAttribute(attributeKey, value);
  7. onChange(s);
  8. }
  9. }

这就是了,实际上我们并未显式的将Session写回redis,而是更新lastAccessTime的时候一并写回去的,而更新访问时间的时候调用了touch()方法,SessionManager又通过SessionDAO读取了一次,重新读取了redis然后反序列化出一个新的Session,原来Session的各种改动自然也就丢失了。

解决

首先是在SessionDAO上加上缓存,一来避免频繁的redis读取,二来避免出现每次读取返回一个新Session的问题。然后在我们的场景中并不需要最后访问时间,因此重写了ShiroFilterFactoryBean,不在更新最后访问时间,当Session需要更新的时候,直接调用SessionDAO写回redis,避免SessionManager做二传手。

当然这不是完美的解决方案,并发场景下依然会有更新问题。调式中可以看出Shiro通过SessionDAO进行的读写操作非常频繁,显然在设计时并未将它当作一个涉及外部IO的类。因此将Session放在redis实则不是一个好注意,应该考虑其它的机制。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号