Thymeleaf自动在URL后加了;jsessionid=的问题

Thymeleaf Add jsessionid in URL

今天在使用Thymeleaf生成HTML文件时,发现某些情况下生成的文件内js、css、图片等资源URL后面被添加上了 ;jsessionid=... 的后缀,导致访问404,HTML页面也无法正确的显示和加载。

问题场景

我在集成了Thymeleaf的Spring Boot项目中,定义了一个 .html 模板文件,并通过以下方式引用了一个css资源:

<link rel="stylesheet" type="text/css" th:href="@{/static/style/qzy.im.css}" />

然后在Controller中添加了一个接口,用来手动将模版渲染成静态HTML文件。

接下来问题就出现了:当我第一次手动调用这个接口后,在生成的静态HTML文件中,css资源的URL后被自动添加上了 ;jsessionid=... 的后缀:

<link rel="stylesheet" type="text/css" href="/static/style/qzy.im.css;jsessionid=AAB59EB2412C150C80EB1F03E2266641" />

这导致这个css请求404,页面无法正确显示样式。

但是,当我第二次调用接口后,;jsessionid=... 的后缀又不见了:

<link rel="stylesheet" type="text/css" href="/static/style/qzy.im.css" />

一切又莫名其妙地恢复了正常😂那么前面的问题是怎么出现的呢?

原因分析

熟悉Java Web开发的人都知道 jsessionid 是Tomcat容器用来维护会话信息的一个东西,我们经常会在请求的Cookie里发现它的身影。但是,为什么它会出现在Thymeleaf渲染后的URL上面呢?

我们只需要跟踪Thymeleaf的渲染过程,在其源码中就能发现端倪:

Thymeleaf的渲染调用栈

上图是渲染过程中的调用栈,最下面是Thymeleaf进入渲染过程,最上面就是执行到了处理URL的地方,我们看一下 StandardLinkBuilder.processLink(IExpressionContext context, String link) 函数的源码:

protected String processLink(final IExpressionContext context, final String link) { if (!(context instanceof IWebContext)) { return link; } final HttpServletResponse response = ((IWebContext)context).getResponse(); return (response != null? response.encodeURL(link) : link); }

从第7行可见,Thymeleaf调用了 HttpServletResponseencodeURL 函数进行URL的重写。而在 HttpServletResponse 的实现类 org.apache.catalina.connector.Response 中我们可以找到具体的实现:

URL重写函数

上面的第1176到1183行就是最关键的部分。其中第1176行通过 isEncodeable 函数来判断是否需要对URL进行替换,如果需要,那么就在第1183行调用 toEncoded 函数在URL中添加了 ;jsessionid=... 的内容:

在URL后添加jsessionid

因此,我们需要重点看一下 isEncodeable 函数的内容以及注释,看看其中具体做了什么判断:

判断是否需要encode

isEncodeable函数

结合函数内容和注释我们可以知道,如果Tomcat容器无法从Cookie中获取当前的Session ID,并且上下文配置中支持从URL中获取会话信息(第1510、1511行),那么就会在URL中添加一个Session ID,即 JSESSIONID。

因此,当浏览器禁用了Cookie、或者我们首次请求没有Cookie时,Thymeleaf渲染后的HTML文件中,URL就会被自动添加上 ;jsessionid=...,这样容器就能通过URL来获取会话信息。但是这并不是我们想要的效果,如何解决这个问题呢?

解决办法

Tomcat可以通过三种方式获取会话信息,这三种方式定义在枚举 javax.servlet.SessionTrackingMode 中:

public enum SessionTrackingMode { COOKIE, URL, SSL }

那么,我们只需要禁止Tomcat从URL中获取会话信息即可,在Spring Boot中,我们只需要在配置文件中添加一个配置:

server.servlet.session.tracking-modes=COOKIE

让Tomcat只能从COOKIE中获取会话信息,这样,当没有Cookie时,URL也就不会被自动添加上 ;jsessionid=... 了。

 

文章评论
${fromAuthor ? '郄正元' : '游客'} 作者 ${gmtCreate}
${content}
${subList.length}
发表评论
${commentToArticle ? '' : parentContent}
字数:0/${maxCommentLength}
该邮箱地址仅用于接收其他用户的回复提醒,不会泄露