Tomcat truncating cookies with = values
Starting with Tomcat 6.0.18 and in Tomcat 7.x any cookie containing a “=” will be truncated. Where you would expect your cookie to hold all values, e.g. “value1=myname&value2=password” you will only be able to retrieve the “value1=”.
This is because Tomcat now adheres to the cookie spec more tightly than previous versions. If you are in control of the cookie you might be able to change it and work around this. If not, then you can can change the settings within the catalina.properties file by appending the following two settings:
org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE=true
org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0=true
案例:
from http://asakakage.blog.163.com/blog/static/212412089201210511111840/
背景:需要从请求中获取某个cookie的内容,在jetty下是正常的,但是tomcat下获取到的该cookie的内容不正确,试了好多次,发现该 cookie的内容从”@“符号之后的部分被截断了。问了好多同事,最后大概了解到”@“不是cookie的合法字符,因此解析出来的cookie的”@ “开始的部分被丢弃了。
--------------------------------------------------------------------------------------
在网上查了下,cookie有以下版本:
版本0:由Netscape公司制定的,也被几乎所有的浏览器支持。Java中为了保持兼容性,目前只支持到版本0,Cookie的内容中不能包含空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号。
版本1:根据RFC 2109(http://www.ietf.org/rfc/rfc2109.txt)文档制定的. 放宽了很多限制. 上面所限制的字符都可以使用. 但为了保持兼容性, 应该尽量避免使用这些特殊字符。
--------------------------------------------------------------------------------------
自己写cookie时,应尽量避免使用上述特殊字符。
读别人写的cookie时,如果遇到含特殊字符的cookie内容,tomcat会只保留特殊字符之前的内容,导致cookie的内容不完整。解决方法有两种:
1、修改tomcat配置
在catalina.properties中加入下面这行配置:
org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0=true
tomcat里有很多配置参数,其中org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0
如果设为true,tomcat将会允许cookie的name和value使用http分隔符(tomcat源码中的http分隔符:'\t', '
', '\"', '(', ')', ',', ':', ';', '<', '=', '>', '?', '@', '[',
'\\', ']', '{', '}',如果设置了org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR这个参数,'/'也会被作为http分隔符),如果没有配置这个参数,默认值为false。参数的详细定义见http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html。
tomcat是用自己的servlet-api,在tomcat的源码里可以看到。它会读取所有的配置参数,并根据参数判断是否允许cookie使用某些特殊字符。
2、在程序中自己解析cookie
可以从request中获取cookie这个header,得到所有cookie的内容字符串,然后再解析出所需要的cookie的内容,示例代码如下:
public static String getCookieValue(String name, HttpServletRequest request){
try {
if(name == null)
return "";
String allCookieStr = request.getHeader("Cookie");
if (allCookieStr != null) {
name = name+"=";
int begin = allCookieStr.indexOf(name);
if (begin >= 0) {
int end = allCookieStr.indexOf(";", begin);
if (end >= 0) {
return allCookieStr.substring(begin + name.length(), end);
}else{
return allCookieStr.substring(fromIndex + name.length());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
--------------------------------------------------------------------------------
如果只是某个特定cookie含有特殊字符,建议采用第二种方法,只针对该cookie调用getCookieValue函数。第一种方法修改了tomcat配置,程序中所有的cookie都会受影响,不保证会不会出现什么问题。。。
案例2
from http://caoyaojun1988-163-com.iteye.com/blog/1997987
一、背景:
公司最近将jboss的版本从4.5 升级到7,之后发现从cookie中,获取不到用户的信息了。经过debug发现,保存用户信息的cookie,在 request.getCookie()之后就是被截断了。本来是 key=x_l=1&x_locale=zh_CN&no_popup_today=n&user=xxx|xxx|xxx|xxx|xxx&xxx=xxx 但是获取到值是key=x_l。
二、相关代码以及问题解决:
在jboss包里 org.apache.tomcat.util.http.Cookies 类里 有如下方法:
- /**
- * Given the starting position of a token, this gets the end of the token, with no separator characters in between.
- * JVK
- */
- private static final int getTokenEndPosition(byte bytes[], int off, int end, int version, boolean isName) {
- int pos = off;
- while (pos < end
- && (!CookieSupport.isHttpSeparator((char) bytes[pos]) || version == 0
- && CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && bytes[pos] != '='
- && !CookieSupport.isV0Separator((char) bytes[pos]) || !isName && bytes[pos] == '='
- && CookieSupport.ALLOW_EQUALS_IN_VALUE)) {
- pos++;
- }
- if (pos > end) return end;
- return pos;
- }
当拿到x_l=1&x_locale=zh_CN&no_popup_today=n& x_user=user=xxx|xxx|xxx|xxx|xxx&xxx=xxx 这个字符串的时候,会通过上面的方法,来找到字符串最后一个 可用字符的位置,然后截断。自然我们获取到key=x_l 也正式这个方法截断导致的。当遇到“=”的时候
bytes[pos] == '=' && CookieSupport.ALLOW_EQUALS_IN_VALUE 这个判断依赖与CookieSupport.ALLOW_EQUALS_IN_VALUE的值。
在CookieSupport 类中可以发现:
- ALLOW_EQUALS_IN_VALUE = Boolean.valueOf(System.getProperty(
- "org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE",
- "false")).booleanValue();
所以找到问题的答案,将 org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE配置为true,及解决问题。
但是为了之前jboss4.5没有这个问题呢? 参见:http://thenitai.com/2013/05/02/tomcat-truncating-cookies-with-values/
三、jboss 7 cookie 解析流程:

四、扩展:
cookie版本的问题:网上很容易找到对应资料,摘录一段:
1. Cookie的兼容性问题
Cookie的格式有2个不同的版本,第一个版本,我们称为Cookie Version
0,是最初由Netscape公司制定的,也被几乎所有的浏览器支持。而较新的版本,Cookie Version 1,则是根据RFC
2109文档制定的。为了确保兼容性,JAVA规定,前面所提到的涉及Cookie的操作都是针对旧版本的Cookie进行的。而新版本的Cookie目
前还不被Javax.servlet.http.Cookie包所支持。
2. Cookie的内容
同样的Cookie的内容的字符限制针对不同的Cookie版本也有不同。在Cookie Version 0中,某些特殊的字符,例如:空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号都不能作为Cookie的内容。
虽然在Cookie Version 1规定中放宽了限制,可以使用这些字符,但是考虑到新版本的Cookie规范目前仍然没有为所有的浏览器所支持,因而为保险起见,我们应该在Cookie的内容中尽量避免使用这些字符。
3、cookie version 1规范
RFC 2109 参见:http://www.faqs.org/rfcs/rfc2109.html
关于cookie中value定义 参见:http://www.faqs.org/rfcs/rfc2068.html
4、cookie vesion 2 规范:http://www.faqs.org/rfcs/rfc2965.html
在 CookieSupport 类也可以看到定义的特殊字符:
- /*
- Excluding the '/' char by default violates the RFC, but
- it looks like a lot of people put '/'
- in unquoted values: '/': ; //47
- '\t':9 ' ':32 '\"':34 '(':40 ')':41 ',':44 ':':58 ';':59 '<':60
- '=':61 '>':62 '?':63 '@':64 '[':91 '\\':92 ']':93 '{':123 '}':125
- */
- if (CookieSupport.FWD_SLASH_IS_SEPARATOR) {
- HTTP_SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',', '/',
- ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
- } else {
- HTTP_SEPARATORS = new char[] { '\t', ' ', '\"', '(', ')', ',',
- ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
- }
浙公网安备 33010602011771号