Thymeleaf自动在URL后加了;jsessionid=的问题
今天在使用Thymeleaf生成HTML文件时,发现某些情况下生成的文件内js、css、图片等资源URL后面被添加上了 ;jsessionid=...
的后缀,导致访问404,HTML页面也无法正确的显示和加载。
问题场景
我在集成了Thymeleaf的Spring Boot项目中,定义了一个 .html
模板文件,并通过以下方式引用了一个css资源:
然后在Controller中添加了一个接口,用来手动将模版渲染成静态HTML文件。
接下来问题就出现了:当我第一次手动调用这个接口后,在生成的静态HTML文件中,css资源的URL后被自动添加上了 ;jsessionid=...
的后缀:
这导致这个css请求404,页面无法正确显示样式。
但是,当我第二次调用接口后,;jsessionid=...
的后缀又不见了:
一切又莫名其妙地恢复了正常😂那么前面的问题是怎么出现的呢?
原因分析
熟悉Java Web开发的人都知道 jsessionid
是Tomcat容器用来维护会话信息的一个东西,我们经常会在请求的Cookie里发现它的身影。但是,为什么它会出现在Thymeleaf渲染后的URL上面呢?
我们只需要跟踪Thymeleaf的渲染过程,在其源码中就能发现端倪:
上图是渲染过程中的调用栈,最下面是Thymeleaf进入渲染过程,最上面就是执行到了处理URL的地方,我们看一下 StandardLinkBuilder.processLink(IExpressionContext context, String link)
函数的源码:
从第7行可见,Thymeleaf调用了 HttpServletResponse
的 encodeURL
函数进行URL的重写。而在 HttpServletResponse
的实现类 org.apache.catalina.connector.Response
中我们可以找到具体的实现:
上面的第1176到1183行就是最关键的部分。其中第1176行通过 isEncodeable
函数来判断是否需要对URL进行替换,如果需要,那么就在第1183行调用 toEncoded
函数在URL中添加了 ;jsessionid=...
的内容:
因此,我们需要重点看一下 isEncodeable
函数的内容以及注释,看看其中具体做了什么判断:
结合函数内容和注释我们可以知道,如果Tomcat容器无法从Cookie中获取当前的Session ID,并且上下文配置中支持从URL中获取会话信息(第1510、1511行),那么就会在URL中添加一个Session ID,即 JSESSIONID。
因此,当浏览器禁用了Cookie、或者我们首次请求没有Cookie时,Thymeleaf渲染后的HTML文件中,URL就会被自动添加上 ;jsessionid=...
,这样容器就能通过URL来获取会话信息。但是这并不是我们想要的效果,如何解决这个问题呢?
解决办法
Tomcat可以通过三种方式获取会话信息,这三种方式定义在枚举 javax.servlet.SessionTrackingMode
中:
那么,我们只需要禁止Tomcat从URL中获取会话信息即可,在Spring Boot中,我们只需要在配置文件中添加一个配置:
让Tomcat只能从COOKIE中获取会话信息,这样,当没有Cookie时,URL也就不会被自动添加上 ;jsessionid=...
了。