一个普通的不能再普通的登录场景:登录成功之后把用户信息放入session:
request.getSession().setAttribute("antiUser", user);
以后使用的时候再从session里面取出:
AntiUser antiUser = (AntiUser)request.getSession().getAttribute("antiUser");
本地运行没有任何问题,开玩笑,搞了那么多年java,这点功能还不是小意思?但是,接下来问题来了,当把项目部署到测试环境上之后,死活都从session里面取不到值,开始郁闷,没有网上说的ajax跨域问题,这到底是为什么呢?
查看f12开发者工具发现如下现象:
图1
再次按f5刷新页面:
图2
大家发现什么问题了吗?两个问题:
1.正常的情况下,对响应头Response Headers来说,只有我们第一次访问该网址时,响应头里面才会有Set-Cookie字段,之后不管你刷新多少次,响应头里面都不会有Set-Cookie字段了。对请求头Request Headers来说,第一次请求时,不发送Cookie字段,之后不管你刷新多少次,请求头都会发送Cookie字段,并且每次都有JSESSIONID这个键和值:
图3
这是正常的流程。
但是,上面每次刷新的时候都有Set-Cookie字段,请求头里没有Cookie字段。请求头不发送JSESSIONID,服务器就认为这是一个新的用户,就会为它分配一次session并把sessionid返回,就是上面Set-Cookie里的JSESSIONID。JSESSIONID的作用就是让后台java程序识别你是谁,通过JSESSIONID找到对应的session。也就是说,可以发现问题所在了:我登录成功之后,把用户信息放入了sessionid=123的session里,下一次请求却到sessionid=456的session里面去找,自然是获取不到呀。
2.问题发现之后,就要探究为什么会出现上面的问题,注意看我截取的Set-Cookie字段,后边有个“Secure; HttpOnly”,我在本地跑了个项目,设置了一个cookie,并没有这个东西啊:
难道是"Secure; HttpOnly"的原因导致了请求头Request Headers每次都不携带JSESSIONID,从而导致服务器认为是新用户而创建新的session?答案是正确的。
当在web.xml中做了如下配置
240
true
之后,浏览器的响应头设置的Set-Cookie字段就如图1所示,会在JSESSIONID后加入“Secure; HttpOnly”,作用就是让请求头在http协议 下的Cookie字段不能携带JSESSIONID这个键,只能在https协议下才能携带,而我测试环境是http协议的,因此才导致了每次请求都创建一个session的问题。我们只需要配置
false
就可以了。记住:只需要在web.xml的cookie-config里面配置
说了这么多,归纳为一句话,
当然,我在java程序里面自己向浏览器加入的cookie也可以控制是否只能在https下才能被请求头的Cookie字段携带。
下面是普通的设置cookie和获取cookie的java代码:
package aaa;
import java.io.IOException;
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("/TestServlet")
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public TestServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
String flag = request.getParameter("flag");
if ("get".equals(flag)) {//获取cookie
Cookie[] cookies = request.getCookies();
if (cookies!=null) {
for(Cookie cookie:cookies){
System.out.println(("key:"+cookie.getName()+",value:"+cookie.getValue()));
}
}
System.out.println("sessionId:"+request.getSession().getId());
}else if("set".equals(flag)){//设置cookie
Cookie cookie1 = new Cookie("key1", "value1");
cookie1.setMaxAge(30); //秒
cookie1.setPath("/");
response.addCookie(cookie1);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
访问“http://localhost:8080/test/TestServlet?flag=set”:
是没有问题的,访问“http://localhost:8080/test/TestServlet?flag=get”:
也可以取到。
修改设置cookie的代码加入cookie1.setSecure(true),如下:
重新启动项目,跟前边一样,访问“http://localhost:8080/test/TestServlet?flag=set”,结果如下:
我的天呢,果然多了个Secure。
我们再获取cookie呢,访问“http://localhost:8080/test/TestServlet?flag=get”:
我们可以看到,在请求头的Cookie字段里面并没有key1键。什么时候才有呢,通过https访问的时候才有。因此要想在http协议下能使用获取到key1,就不要给他设置setSecure(true)。
需要注意的是,自己设置的属性跟
里面的secure是true还是false没有关系,secure是true还是false只控制Cookie字段里面有没有JSESSIONID键。