Cookie:java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
使用servlet写一个案例:显示用户的上次访问时间分析
爆出一下的错误:
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
控制台输出如下:
严重: Servlet.service() for servlet [com.zhiyou.servlet.lastaccesstime.LastAccessTimeServlet] in context with path [/stu04] threw exception
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
at org.apache.tomcat.util.http.Rfc6265CookieProcessor.validateCookieValue(Rfc6265CookieProcessor.java:182)
at org.apache.tomcat.util.http.Rfc6265CookieProcessor.generateHeader(Rfc6265CookieProcessor.java:115)
at org.apache.catalina.connector.Response.generateCookieString(Response.java:976)
at org.apache.catalina.connector.Response.addCookie(Response.java:928)
at org.apache.catalina.connector.ResponseFacade.addCookie(ResponseFacade.java:385)
at com.zhiyou.servlet.lastaccesstime.LastAccessTimeServlet.doGet(LastAccessTimeServlet.java:31)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:770)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
了解到 An invalid character [32] was present in the Cookie value 中32对应的编码是空格,Stack Overflow上的回答:This is due to Tomcat’s cookie processing being changed to a RFC 6265 compliant implementation by default in 8.5, which does not allow space (character 32), among others.
tomcat版本是9.0,cookie存的值确实有空格,改用编码后再存入cookie,可避免出现空格。
修改后的代码如下:
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/lastAccessTime")
public class LastAccessTimeServlet extends HttpServlet {
/**
* serialVersionUID:
*/
private static final long serialVersionUID = -3666204851005119865L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
//获得当前时间
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String currentTime = format.format(date);
//1、创建Cookie 记录当前的最新的访问时间
// Cookie cookie = new Cookie("lastAccessTime",currentTime);//报错地方
//deBug:格式化后的date值,通过编码存入cookie
Cookie cookie = new Cookie("lastAccessTime",URLEncoder.encode(currentTime,"UTF-8"));
cookie.setMaxAge(60*10*500);
response.addCookie(cookie);
//2、获得客户端携带cookie ---- lastAccessTime
String lastAccessTime = null;
Cookie[] cookies = request.getCookies();
if(cookies!=null){
for(Cookie coo : cookies){
if("lastAccessTime".equals(coo.getName())){
lastAccessTime = coo.getValue();
}
}
}
response.setContentType("text/html;charset=UTF-8");
if(lastAccessTime==null){
response.getWriter().write("您是第一次访问");
}else{
//
response.getWriter().write("您上次的访问的时间是:"+lastAccessTime);
//deBug:cookie取出的值,通过解码显示到页面上
response.getWriter().write("您上次的访问的时间是:"+URLDecoder.decode(lastAccessTime,"UTF-8"));
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
归纳总结
关于使用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的内容中尽量避免使用这些字符。
RFC2109 制定的规范:
RFC 2068 制定的规范:
Cookie中永远不要存特殊字符,即使要存储也要进行编码以后再存。
【参考】
HTTP cookie: https://en.wikipedia.org/wiki/HTTP_cookie
关于cookie特殊字符的一点理解: http://www.blogjava.net/stone2083/archive/2010/11/03/336923.html