今天在代码中调用 HttpServletRequest 对象的 getCookies() 方法时,发现实际得到的 Cookie 数量与提交的不符。实际提交了 17 个 Cookie,但是获取到的只有 14 个。
经过排查,发现如果调用 getHeaders(“Cookie”) 方法,获取原始的 Cookie 串,是可以拿到正确的 17 个 Cookie 组成的字符串的。于是确认应该是 Tomcat 在处理 Cookie 的时候进行了过滤。
经过一番搜索,发现了这个文档:
http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html
其中,跟 Cookie 相关的参数有:
org.apache.tomcat.util.http. ServerCookie.ALLOW_EQUALS_IN_VALUE |
If this is true Tomcat will allow ‘= ‘ characters when parsing unquoted cookie values. If false , cookie values containing ‘= ‘ will be terminated when the ‘= ‘ is encountered and the remainder of the cookie value will be dropped.
If not specified, the default value specification compliant value of |
org.apache.tomcat.util.http. ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0 |
If this is true Tomcat will allow HTTP separators in cookie names and values.
If not specified, the default specification compliant value of |
org.apache.tomcat.util.http. ServerCookie.ALLOW_NAME_ONLY |
If this is false then the requirements of the cookie specifications that cookies must have values will be enforced and cookies consisting only of a name but no value will be ignored.
If not specified, the default specification compliant value of |
org.apache.tomcat.util.http. ServerCookie.ALWAYS_ADD_EXPIRES |
If this is true Tomcat will always add an expires parameter to a SetCookie header even for cookies with version greater than zero. This is to work around a known IE6 and IE7 bug that causes IE to ignore the Max-Age parameter in a SetCookie header.
If |
org.apache.tomcat.util.http. ServerCookie.FWD_SLASH_IS_SEPARATOR |
If this is true then the / (forward slash) character will be treated as a separator. Note that this character is frequently used in cookie path attributes and some browsers will fail to process a cookie if the path attribute is quoted as is required by a strict adherence to the specifications. This is highly likely to break session tracking using cookies.
If |
org.apache.tomcat.util.http. ServerCookie.PRESERVE_COOKIE_HEADER |
If this is true Tomcat will ensure that cookie processing does not modify cookie header returned by HttpServletRequest.getHeader() .
If |
org.apache.tomcat.util.http. ServerCookie.STRICT_NAMING |
If this is true then the requirements of the Servlet specification that Cookie names must adhere to RFC2109 (no use of separators) will be enforced.
If |
看了一下相关参数,跟我这个现象有关的是这两个选项:
org.apache.tomcat.util.http. ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0 org.apache.tomcat.util.http. ServerCookie.ALLOW_NAME_ONLY
于是按照这个选项去搜索,发现了这样一篇文章:
http://www.cnblogs.com/princessd8251/articles/4172103.html
其中详细说明了第一个选项的作用:
在网上查了下,cookie有以下版本:
版本0:由Netscape公司制定的,也被几乎所有的浏览器支持。Java中为了保持兼容性,目前只支持到版本0,Cookie的内容中不能包含空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号。
版本1:根据RFC 2109(http://www.ietf.org/rfc/rfc2109.txt)文档制定的. 放宽了很多限制. 上面所限制的字符都可以使用. 但为了保持兼容性, 应该尽量避免使用这些特殊字符。
Tomcat 具体的过滤源码里面包含了这些字符:
如果 org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR 设为 true (默认 False),’/’也会被作为http分隔符
如果 org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0 设为 true (默认 False),tomcat将会允许cookie的name和value使用http分隔符
tomcat源码中的http分隔符:’\t’, ‘ ‘, ‘\”‘, ‘(‘, ‘)’, ‘,’, ‘:’, ‘;’, ‘<', '=', '>‘, ‘?’, ‘@’, ‘[‘, ‘\\’, ‘]’, ‘{‘, ‘}’
然后第二个选项就很好理解了,如果 Cookie 的 Value 是空,那么直接干掉。
于是,我们传的 Cookie 是这样的:
aaa=@abcdefg
Tomcat 先做了一次截断,将 @ 和后面的内容都去掉了;然后做了一次判断,发现 aaa 这个 Cookie 只有 name 没有 value,于是直接干掉了。
到此真相大白。
如果想要解决这个问题,有两个方法:
1. 在 Tomcat 的配置文件 catalina.properties 中设置 org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0=true
2. 在代码中使用 request.getHeader(“Cookie”) 取出原始的 Cookie 串,自行处理。