Tomcat 8.5.53特殊字符乱码问题:重启后自动恢复
遇到过好多次这类Tomcat偶发编码乱码、重启后自动恢复的情况,给你拆解分析下这两个问题的根因和靠谱的解决方案:
问题1:Web应用返回UTF-8编码特殊字符显示为????
现象回顾
Web应用返回的UTF-8编码特殊字符(比如'、é)显示为????,重启Tomcat后恢复正常。
可能根因
- Tomcat的响应编码未全局强制锁定,当Connector的编码缓存出现异常时,默认切换到了不支持UTF-8的编码(比如ISO-8859-1)
- 应用内的JSP/Servlet未正确设置响应的
contentType为text/html;charset=UTF-8,且Tomcat全局响应编码配置未生效,重启后编码缓存重置,临时恢复正常
解决方案
全局配置Tomcat Connector编码
修改conf/server.xml中的Connector节点,添加URIEncoding="UTF-8"和useBodyEncodingForURI="true",确保请求和响应的编码统一:<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" useBodyEncodingForURI="true"/>添加应用级编码过滤器
在应用的web.xml中添加全局编码过滤器,强制所有请求和响应使用UTF-8:<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>如果不是Spring应用,可以自定义一个简单的编码过滤器,核心逻辑是设置
request.setCharacterEncoding("UTF-8")和response.setCharacterEncoding("UTF-8")。确保JSP页面编码正确
所有JSP页面开头必须设置:<%@ page contentType="text/html;charset=UTF-8" language="java" %>
问题2:POST提交特殊字符用户名后Cookie解密乱码
现象回顾
部署在Tomcat 8.5.53的应用,POST提交含特殊字符的用户名(比如Amélie.Lacoste),对应的Cookie经Base64编码+AES加密后,解密解码结果显示为Am?lie.Lacoste,重启Tomcat后恢复正常。
可能根因
- 核心问题是POST请求参数的编码未被强制设置,当Tomcat的参数解析缓存异常时,用了默认的ISO-8859-1编码解析POST参数,导致特殊字符被损坏后存入Cookie;重启后缓存重置,临时恢复了正确的UTF-8解析
- Cookie的编码处理未显式指定UTF-8,Tomcat默认的Cookie编码在缓存异常时出现乱码
解决方案
强化Tomcat默认Servlet编码配置
修改conf/web.xml中的defaultServlet,添加编码参数:<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>显式处理Cookie的编码解码
在设置Cookie值时,手动用UTF-8编码:String cookieValue = URLEncoder.encode(encryptedUsername, "UTF-8"); Cookie userCookie = new Cookie("user", cookieValue); response.addCookie(userCookie);获取Cookie值时,用UTF-8解码:
String cookieValue = URLDecoder.decode(cookie.getValue(), "UTF-8"); String decryptedUsername = decryptAndDecode(cookieValue); // 你的解密解码逻辑锁定JVM层面的默认编码
在Tomcat的启动脚本(比如bin/catalina.sh或bin/catalina.bat)中添加JVM参数,确保JVM默认编码为UTF-8:# Linux/Mac 在catalina.sh中添加 JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"# Windows 在catalina.bat中添加 set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8
总结
这类偶发的重启自愈编码问题,本质是Tomcat的编码相关缓存(Connector编码缓存、参数解析缓存)出现异常,导致编码切换到了不支持UTF-8的格式。通过从Tomcat全局配置、应用代码配置、JVM参数三个层面强制锁定UTF-8编码,就能彻底避免这类问题再次发生,不用再依赖重启临时解决。
内容的提问来源于stack exchange,提问作者Akshay Gaba

