Tomcat 在处理 Cookie 的时候的几个小坑

今天在代码中调用 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 false will be used.

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 false will be used.

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 false will be used.

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.catalina.STRICT_SERVLET_COMPLIANCE is set to true, the default of this setting will be false, else the default value will be true.

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.catalina.STRICT_SERVLET_COMPLIANCE is set to true, the default of this setting will be true, else the default value will be false.

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.catalina.STRICT_SERVLET_COMPLIANCE is set to true, the default of this setting will be true, else the default value will be false.

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.catalina.STRICT_SERVLET_COMPLIANCE is set to true, the default of this setting will be true, else the default value will be false.

看了一下相关参数,跟我这个现象有关的是这两个选项:

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 串,自行处理。

使用Apache的mod_headers和mod_setenvif为静态文件取消Cookie

因为静态图片一般都不需要使用Cookie,因此,将静态文件的SetCookie头都去掉是一个很好的加快网站访问速度的办法。使用Apache的话,可以用mod_headers和mod_setenvif两个模块实现。

首先在httpd.conf中加载这两个模块,然后写上如下代码:
SetEnvIf mime image/.* unset-cookie
Header unset Set-Cookie env=unset-cookie

这样,当Apache再遇到类型为图片的文件时,就不会发送Set-Cookie头,自然可以减少一点流量的开销,加快网站的访问速度。