首页 > 其他分享 >SpringBoot Session共享,配置不生效问题排查 → 你竟然在代码里下毒!

SpringBoot Session共享,配置不生效问题排查 → 你竟然在代码里下毒!

时间:2024-08-05 08:56:03浏览次数:15  
标签:sessionRepository SpringBoot Session RedisConfig RedisHttpSessionConfiguration p

开心一刻

快 8 点了,街边卖油条的还没来,我只能给他打电话

大哥在电话中说到:劳资卖了这么多年油条,从来都是自由自在,自从特么认识了你,居然让我有了上班的感觉!

你让我有了上班的感觉

Session 共享

SpringBoot session 共享配置,我相信你们都会,但出于负责的态度,我还是给你们演示一遍

  1. 添加依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.qsl</groupId>
        <artifactId>spring-boot-session-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.7.18</version>
        </parent>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
            </dependency>
        </dependencies>
    </project>
    
  2. 添加配置

    文件配置 application.yml

    spring:
      session:
        store-type: redis
      redis:
        timeout: 3000
        password: 123456
        host: 10.5.108.226
        port: 6379
    

    注解配置

    @SpringBootApplication
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 900, redisNamespace = "session-demo")
    public class SessionApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SessionApplication.class, args);
        }
    }
    

    两个配置项需要说明下

    maxInactiveIntervalInSeconds:session 有效时长,单位是秒,示例中 session 有效时长是 900s

    redisNamespace:redis 命名空间,即将 session 信息存于 redis 的哪个命名空间下,没有会创建,示例中是 session-demo

  3. 操作 session

    为了简化,直接提供接口设置和访问 session

    @RestController
    @RequestMapping("hello")
    public class HelloController {
        @GetMapping("/set")
        public String set(HttpSession session) {
            session.setAttribute("user", "qsl");
            return "qsl";
        }
        @GetMapping("/get")
        public String get(HttpSession session) {
            return session.getAttribute("user") + "";
        }
    }
    

至此,搭建就算完成了,启动后访问

http://localhost:8080/hello/set

然后去 redis 看 session 信息

redis_session

有效时长为什么是 870 而不是 900,请把头伸过来,我悄悄告诉你

20230115143049

我就问你们,SpringBoot Session 共享是不是很简单?但就是这么简单的内容,竟然有人往里面下毒,而我很不幸的成了那个中毒之人,如果不是我有绝招,说不定就噶过去了,具体细节且听我慢慢道来

配置不生效

实际项目中,我也是按如上配置的,可 redis 中的存放内容却是

异现象

从结果来看,session 确实是共享了,但为什么 maxInactiveIntervalInSecondsredisNamespace 配置都未生效?我还特意去对比了另外一个项目,一样的配置流程,那个项目的 命名空间有效时长 都是正常生效的,而此项目却未生效,这就让我彻底懵圈了

懵

debug 源码

该尝试的都尝试了,maxInactiveIntervalInSecondsredisNamespace 始终不生效,没有办法了,只能上绝招了

debug 调试源码

问题又来了:断点打在哪?有两个地方需要打断点

  1. RedisHttpSessionConfiguration#sessionRepository

    跟进到 @EnableRedisHttpSession 注解里面,会看到 @Import(RedisHttpSessionConfiguration.class),跟进 RedisHttpSessionConfiguration,会看到被 @Bean 修饰的 sessionRepository 方法,正常情况下,SpringBoot 启动过程中会调用该方法,我们在该方法第一行打个断点

    sessionRepository 断点
  2. SpringHttpSessionConfiguration#springSessionRepositoryFilter

    注意看 RedisHttpSessionConfiguration 的完整定义

    @Configuration(proxyBeanMethods = false)
    public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
    		implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware
    

    它继承 SpringHttpSessionConfiguration,跟进去你会发现有个被 @Bean 修饰的 springSessionRepositoryFilter 方法,正常情况下,SpringBoot 启动过程中也会调用该方法,我们也在该方法第一行打个断点

    springSessionRepositoryFilter 断点

打完断点后,重新以 debug 方式进行启动,我们会发现最先来到 springSessionRepositoryFilter 的断点

springSessionRepositoryFilter 断点进入

然后我们按 F9,会发现项目启动完了都没有来到 RedisHttpSessionConfiguration#sessionRepository 的断点,这是为什么?SpringHttpSessionConfiguration#springSessionRepositoryFilter 方法有个参数 SessionRepository<S> sessionRepository,它依赖 RedisIndexedSessionRepository 实例,也就说 RedisHttpSessionConfiguration#sessionRepository 应该被先调用,sessionRepository 方法都没有被调用,那 springSessionRepositoryFilter 方法的参数实例是个什么鬼?我们再次以 debug 方式启动

springSessionRepositoryFilter 参数

怎么是 RedisOperationsSessionRepository,为什么不是 RedisIndexedSessionRepository ?我们来看看 RedisOperationsSessionRepository

RedisOperationsSessionRepository

它继承了 RedisIndexedSessionRepository,重点是它被 @Deprecated 了呀,怎么还会创建该类型的实例,它是哪里被实例化了?按住 ctrl 键,鼠标左击 RedisOperationsSessionRepository

RedisOperationsSessionRepository 被调用

点进 RedisConfig 一看吓一跳

RedisOperationsSessionRepository 实例化

一看提交记录,竟然是 2021-09-26 提交的,一看提交人,好家伙,早就离职了!

代码里下毒

我估摸着,当初想做 session 共享,但是开发到了一半,直接离职了,你说你离职就离职吧,为什么要提交这一半代码,真的是,气的我牙都咬碎了!

注释掉 RedisConfig 后重启,一切恢复正常,maxInactiveIntervalInSecondsredisNamespace 都正常生效;实际工作开发中,此事就完结了,不要再去细扣了,除非你确实闲的蛋疼。但话说回来,你们都来看博客了,那确实是闲,既然你们这么闲,那我们继续扣一扣,扣什么呢

为什么我们指定 RedisOperationsSessionRepository 后,RedisHttpSessionConfiguration#sessionRepository 方法不被调用,而且 maxInactiveIntervalInSeconds 、redisNamespace 不生效

  1. RedisHttpSessionConfiguration#sessionRepository 为什么没被调用

    不管是我们自定义的 RedisConfig#redisOperationsSessionRepository,还是 SpringBoot 的 RedisHttpSessionConfiguration#sessionRepository,都会在启动过程中被 SpringBoot 解析成 BeanDefinition,至于如何解析的,这就涉及到 @Configuration 的解析原理,不了解的可以先看看:spring-boot-2.0.3源码篇 - @Configuration、Condition与@Conditional 。另外,BeanDefinition 的扫描是有先后顺序的,详情请看:三探循环依赖 → 记一次线上偶现的循环依赖问题

    回到我们的案例,那么 RedisConfig#redisOperationsSessionRepository 会先于 RedisHttpSessionConfiguration#sessionRepository 扫描成 BeanDefinition

    bean定义顺序

    而紧接着的 bean 实例化就是按着这个顺序进行的,也就说 RedisConfig#redisOperationsSessionRepository 会先被调用;我们把重点放到名字叫做 sessionRepository 的 bean 的实例化过程上。这里补充个 debug 小技巧,因为 bean 很多,而我们只关注其中某个 bean 的实例化,可以借助 IDEA 的 Condition 来实现

    idea 条件debug

    然后按 F9,会直接来到 sessionRepository 实例化过程,然后经过 getBean(String name) 来到 doGetBean

    doGetBeanpng

    跟进 transformedBeanName 方法,继续跟进来到 canonicalName

    anonialName

    这是重点,大家看仔细了,根据别名递归读取主名,返回最后那个主名,是不是这么个逻辑?然而新的疑问又来了

    哪来的别名、主名呀

    常规情况下,bean 只有一个名字,也就是主名,使用 @Bean 的时候如果没有指定名字,那么名字默认就是方法名,而如果指定了名字就采用指定的名字;支持指定多个名字,第一个是主名,后面的都是别名

    主名别名 aliasMap

    所以,根据别名 sessionRepository 就得到了 redisOperationsSessionRepository 这个主名

    sessionRepository被替换OperationsSessionRepository

    而名叫 redisOperationsSessionRepository 的 bean 已经被创建过了,类型是 RedisOperationsSessionRepository,直接从容器中获取,然后返回;所以 RedisHttpSessionConfiguration#sessionRepository 没被调用,你们明白了吗?

    回到最初的问题,如果不注释 RedisConfig,而只是拿掉别名 sessionRepository

    @Configuration
    public class RedisConfig {
    
    	@Autowired
    	private RedisTemplate redisTemplate;
    
    	@Bean({"redisOperationsSessionRepository"})
    	public RedisOperationsSessionRepository redisOperationsSessionRepository() {
    		return new RedisOperationsSessionRepository(redisTemplate);
    	}
    }
    

    问题能不能得到解决?

    总结下

    根据扫描先后循序,RedisConfig#redisOperationsSessionRepository 的 BeanDefinition 排在 RedisHttpSessionConfiguration#sessionRepository 前面,所以 bean 实例创建的时候,RedisOperationsSessionRepository 实例会被先创建,而这个实例的别名 sessionRepository 正好与 RedisHttpSessionConfiguration#sessionRepository 名字重复,所以不会调用 RedisHttpSessionConfiguration#sessionRepository 来创建实例,而是直接返回已经创建好的 RedisOperationsSessionRepository 实例

  2. maxInactiveIntervalInSeconds 、redisNamespace 为什么不生效

    大家注意看 RedisConfig

    @Configuration
    public class RedisConfig {
    
    	@Autowired
    	private RedisTemplate redisTemplate;
    
    	@Bean({"redisOperationsSessionRepository", "sessionRepository"})
    	public RedisOperationsSessionRepository redisOperationsSessionRepository() {
    		return new RedisOperationsSessionRepository(redisTemplate);
    	}
    }
    

    试问如何让 maxInactiveIntervalInSeconds 、redisNamespace 生效?

    既然官方已经把 RedisOperationsSessionRepository 废弃了,我们就不要纠结它了,直接不用它!

总结

  1. SpringBoot Session 共享配置很简单,如果配置好了结果不对,不要怀疑自己,肯定是有人在代码里下毒了
  2. 压箱底的东西(debug 源码)虽说不推荐用,但确实是一个万能的方法,不要求你们精通,但必须掌握
  3. 作为一个开发者,一定要有职业素养,开发一半的代码就不要提交了,着实坑人呀!

标签:sessionRepository,SpringBoot,Session,RedisConfig,RedisHttpSessionConfiguration,p
From: https://www.cnblogs.com/youzhibing/p/18340999

相关文章

  • 计算机Java项目|基于SpringBoot的党员教育和管理系统的设计与实现
    作者主页:编程指南针作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与......
  • SpringBoot项目配置多数据源-Transcational事务篇
    结论通过多次实践,在执行业务代码场景中,Spring事务并不能同时管理两个数据源的事务,只能管理单个数据源的事务。如果确实有需要同时操作两个数据源,而且需要事务的场景,可以trycatah起来,然后判断需不需要对数据处理(模拟回滚)。上一篇:SpringBoot项目配置多数据源,结合mybatis-pl......
  • SpringBoot + Vue + ElementUI 的人力资源管理系统-附项目源码与配套文档
    摘要在如今这个人才需求量大的时代,各方企业为了永葆企业的活力与生机,在不断开拓进取的同时,又广泛纳用人才,为企业的长久发展奠定了基础。于是,各个企业与部门机构,都不可避免地会接触到人力资源管理的问题。Hrm是一款人力资源管理系统,其主要功能模块有员工个人信息修改、请......
  • Springboot计算机毕业设计城市地铁线路与站点查询系统+程序+源码+数据库+调试部署+开
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,车次查询,站点信息,附近公交信息开题报告内容一、研究背景与意义随着城市化进程的加快和人口的不断增长,城市交通问题日益突出。城市轨道交通作为一种高效......
  • Springboot计算机毕业设计成都医学院学生实习管理系统的设计与实现
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表学生,实习单位,实习总结,考勤打卡,实习信息,实习评价,学生沟通,单位沟通,学生简历开题报告内容一、研究背景与意义随着社会的发展和医学教育的不断进步,实习已......
  • Springboot计算机毕业设计超市进销存管理系统7cq1l
     本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,商家,商品分类,商品信息,商品库存,商品入库,商品出库,在线留言,通知公告开题报告内容一、摘要本文旨在设计并实现一个超市进销存管理系统,该系统通过集成......
  • SpringBoot2.7.18拦截器失效不起作用
    这几天在做项目,从其他项目中复制粘贴拦截器的代码,然后修修改改,但是拦截器一直不起作用,请求来了进不去,最后发现是我写错了,代码如下:配置文件:application.ymlserver:port:8080servlet:context-path:/api/v1#springboot的配置spring:datasource:#定义数据源......
  • springboot+vue景区共享电车管理系统【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景随着旅游业的蓬勃发展,景区游客数量持续增长,传统交通方式已难以满足游客便捷、环保的出行需求。在此背景下,景区共享电车作为一种新兴的绿色出行方式,凭借其灵活便捷、低碳环保的特点,逐渐成为景区管理与游客出行的优选方案。然而,随着共享......
  • springboot+vue精品课程网站的设计与实现【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展和教育理念的不断革新,精品课程网站作为现代教育资源的重要载体,正逐步成为推动高等教育质量提升的关键力量。当前,高校课程资源的数字化、网络化已成为不可逆转的趋势,学生与教师对于便捷、高效、互动性强的学习平......
  • springboot+vue精点味道蛋糕房管理系统【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景在快节奏的现代生活中,烘焙食品以其独特的口感与精美的外观,成为了人们日常生活中不可或缺的甜蜜伴侣。精点味道蛋糕房作为本地知名的烘焙品牌,凭借其精湛的技艺和丰富的产品线赢得了广大消费者的喜爱。然而,随着业务的不断扩展和顾客需求......