本篇文章给大家谈谈SpringBoot异步线程获取 Session 解决方案,以及对应的知识点,文章可能有点长,但是希望大家可以阅读完,增长自己的知识,最重要的是希望对各位有所帮助,可以解决了您的问题,不要忘了收藏本站喔。
我们将原有项目的登录授权功能从Shiro切换到接入SSO单点登录服务并非一帆风顺,因为系统多了,总有一些让我们预想不到的骚操作。
比如这个,在处理请求的线程上启动一个线程,在这个新的线程中获取Session,从Session获取登录用户。
这真的是一个骚操作了!
由于未考虑到这种情况,上线就出了Bug,好在不是很严重的问题。
我们不讨论这样的骚操作有何不好,重要的如何解决问题?找到各种这样的骚操作修改过来?难!没有那么多时间慢慢改,而且改完还需要走一遍测试流程。
关于这件事情我跟同事争论了一番,毕竟之前是没有出现这个问题的,而就切换到SSO(为方便接入SSO服务而封装的SDK)就出现问题,不是我的问题又是谁的问题呢。
上图的代码经测试确实没有问题,测试结果如下图所示。
首先我们要知道,Session ID由服务端创建,并通过响应头cookie响应给浏览器,浏览器将Session ID存储在本地,会在下次请求自动带上Session ID,通过cookie请求头发送给服务端。
在服务端接收到客户端请求时,如果客户端有带上Session ID,那么就根据Session ID获取Session,默认是从内存获取,如果是使用Shiro框架并且使用Redis存储Session,那么就是从Redis中获取。
如果Session过期或者客户端没有传递Session ID,则创建新的Session,并会为新的Session重新分配Session ID。
Shrio框架在接收到请求时就通过过滤器拿到Session ID存储到ThreadLocal中了,所以不需要通过HttpServletRequest去拿Session。
Shrio之所以支持在异步线程中还能够获取到Session,这其实是因为Shrio使用的是InheritableThreadLocal,而不是ThreadLocal,实现了将Session ID传递给子线程,因此实现了“异步上下文”传递Session。
但这也是有局限的,要求这个异步线程必须是由当前处理请求的线程创建的,Session ID才能通过InheritableThreadLocal传递给子线程,如果是在线程池中,就不一定能获取到了。
这里留个思考题给大家:为什么说在线程池中不一定能获取到,而不是一定获取不到?要理解这个问题需要对线程池的工作原理、源码,以及InheritableThreadLocal源码理解,因此本篇不展开分析。
所以我们解决Bug就是将ThreadLocal换成InheritableThreadLocal,并且通过方法拦截器(HandlerInterceptor)或者过滤器(Filter)实现set session和remove session操作,推荐后者。
另外,从webmcv框架源码可以看出,RequestContextHolder#getRequestAttributes也是支持InheritableThreadLocal的,只是默认情况下不支持,需要修改配置。
getRequestAttributes方法会尝试从InheritableThreadLocal获取,源码如下。
但能不能获取得到由是否写入决定:
setRequestAttributes方法由RequestContextFilter过滤器调用,该过滤器由webmvc框架自动配置,代码如下。
默认RequestContextFilter并不会将ServletRequestAttributes写入InheritableThreadLocal,代码如下。
因此,我们是否可以替换默认注册的RequestContextFilter,将threadContextInheritable配置为true,这样就能支持将Session ID传递给子线程了,如下代码所示。
但,这并不是有效的,因为在RequestContextFilter之后,DispatcherServlet又调用了一次RequestContextHolder#setRequestAttributes,并且传入的threadContextInheritable为false,清除了前面的写入,所以必须要修改DispatcherServlet的threadContextInheritable为true才支持。但不建议在封装的SDK中这样改动。
关于为什么threadContextInheritable默认为false,官方在RequestContextFilter的API文档给出了如下说明。
OK,关于SpringBoot异步线程获取 Session 解决方案和的内容到此结束了,希望对大家有所帮助。
本文采摘于网络,不代表本站立场,转载联系作者并注明出处:https://www.iotsj.com//kuaixun/7330.html
用户评论
这篇文章终于解决了我的一个困扰,我以前在异步线程里总是找不到session!
有10位网友表示赞同!
Spring boot 学习太难了,这篇文章讲解得真细致
有10位网友表示赞同!
感觉自己最近对SpringBoot的理解更深一层了,学习笔记又要更新啦!
有14位网友表示赞同!
异步线程和Session一直让我头疼,这下终于找到了解决方法
有10位网友表示赞同!
遇到同样的问题,还好这篇文章解答了我的疑惑
有10位网友表示赞同!
这篇文章刚好解决了我现在在项目中遇到的一个坑!厉害!
有7位网友表示赞同!
分享一下我自己的解决方法,希望也能帮到其他遇到相同问题的同学。
有5位网友表示赞同!
学习SpringBootやっぱり需要不断地积累经验和查找资料,这篇文章很有价值
有13位网友表示赞同!
最近一直在整理SpringBoot的相关知识点,这篇文章刚好可以补充我的笔记
有12位网友表示赞同!
异步线程真是个麻烦的玩意儿,不过看了这篇文的解决方法感觉可行!
有20位网友表示赞同!
学习开发的过程中遇到各种问题很正常,关键是找到解决方法!这篇文章就是很好的案例
有11位网友表示赞同!
SESSION 是Spring Boot 开发中重要的一部分,掌握正确的方法才能避免很多坑
有10位网友表示赞同!
感谢作者分享这篇实用的文章!解决了我的困扰真是太棒了!
有19位网友表示赞同!
SpringBoot学习需要不断地实践和总结,这篇文章帮我巩固了一些知识点
有6位网友表示赞同!
每次遇到技术难题都会查阅相关资料,这次是这篇Spring Boot的文章真的很有用。
有13位网友表示赞同!
异步线程和SESSION 的配合确实有很多需要注意的地方,看了这篇文后更加理解了!
有20位网友表示赞同!
这篇文章让我对SpringBoot的理解更深一层,学习之路永不停歇!
有14位网友表示赞同!
分享一下我的学习成果,希望可以帮助到其他正在学Spring Boot的小伙伴们
有15位网友表示赞同!
学习编程需要不断钻研和实践,这篇文章为我提供了很多启发!
有13位网友表示赞同!