Session 和 Cookie,你真的清楚了?看完再说!
从事 web 开发同学,相信对 session 和 cookie 都不会感到陌生,可是你真正理解 session 和 cookie 以及两者的区别吗?曾经面试过一个十年经验的开发竟然也回答的模棱两可,所以觉得有必要详细理一下。
在正式开始本文内容前,需要对JavaEE规范有一定的认识:
规范中的 servlet 是sun定义的用于开发服务端的接口,如HttpServletRequest、 Cookie、HttpSession等,但是实现由具体容器决定,比如 tomcat、jetty等。
下面正式开始今天内容,首先看下session 是什么?
通常一次连接建立,默认服务器端就产生了一个会话,这个跟现实世界里打电话类似:一旦通话双方接通后就代表一次会话开始了。需要注意的是现实通话双发一旦挂断电话,就代表会话结束了,但是我们这里的 session 却是跨多个连接的!
我们再看下 cookie 的定义(wikipedia):
An HTTP cookie (also called web cookie, Internet cookie, browser cookie, or simply cookie) is a small piece of data sent from a website and stored on the user’s computer by the user’s web browser while the user is browsing.
翻译过来一句话:cookie是由服务端发送过来,浏览器存储的数据片。
定义我们清楚了,下面看 session 和 cookie 的关系:session 创建后会将 session id (准确名称 JSESSIONID) 放入 cookie 中存储在浏览器中。这是大家印象里的,但事实是:不手动调用 获取 session 方法, 并不会产生 session 对象,也不会创建cookie!
看如下代码:
@GetMapping("/versions")
public ResponseEntity selectAll(@RequestParam("device_type") String device_type, HttpServletRequest request) {
System.out.println(request.getClass());
// cookie为空
Cookie[] cookies = request.getCookies();
if(cookies !=null) {
Arrays.stream(cookies).forEach((cookie) ->
log.info(cookie.getName() + " : " + cookie.getValue())
);
}
OTAVersionExample example = new OTAVersionExample();
example.createCriteria().andDevice_typeEqualTo(device_type);
List<OTAVersion> otaVersionList = otaVersionMapper.selectByExample(example);
return Ok.newOk(otaVersionList, HttpStatus.OK);
}
以上代码,无论你访问多少次,在控制台 Request headers 始终没有cookie, 后台代码也不会打印 cookie 信息。
现修改代码:
@GetMapping("/versions")
public ResponseEntity selectAll(@RequestParam("device_type") String device_type, HttpServletRequest request) {
System.out.println(request.getClass()); // class org.eclipse.jetty.server.Request
HttpSession session = request.getSession(); // 手动创建session,cookie才有值
Cookie[] cookies = request.getCookies();
if(cookies !=null) {
Arrays.stream(cookies).forEach((cookie) ->
// JSESSIONID : 1D836B3B8D71A92D23305A62A7A3F929log.info(cookie.getName() + " : " + cookie.getValue()));}OTAVersionExample example = new OTAVersionExample();example.createCriteria().andDevice_typeEqualTo(device_type);List<OTAVersion> otaVersionList = otaVersionMapper.selectByExample(example);return Ok.newOk(otaVersionList, HttpStatus.OK);}
首次请求, 没有 cookie信息;刷新后, Request headers 中多了一句:
Cookie:: JSESSIONID=1D836B3B8D71A92D23305A62A7A3F929
深入源码,看下 getSession 方法到底做了什么:
org.eclipse.jetty.server.Request
public class Request implements HttpServletRequest
{
....
@Override
public HttpSession getSession()
{
return getSession(true);
}
@Override
public HttpSession getSession(boolean create)
{
if (_session != null)
{
if (_sessionManager != null && !_sessionManager.isValid(_session))
_session = null;
else
return _session;
}
if (!create)
return null;
if (getResponse().isCommitted())
throw new IllegalStateException("Response is committed");
if (_sessionManager == null)
throw new IllegalStateException("No SessionManager");
_session = _sessionManager.newHttpSession(this);
HttpCookie cookie = _sessionManager.getSessionCookie(_session,getContextPath(),isSecure()); //为session添加cookie
if (cookie != null)
_channel.getResponse().addCookie(cookie); //看到了添加cookie到response消息中
return _session;
}
.....
@Override
public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
{
if (isUsingCookies())
{
String sessionPath = (_cookieConfig.getPath()==null) ? contextPath : _cookieConfig.getPath();
sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
String id = getNodeId(session);
HttpCookie cookie = null;
if (_sessionComment == null)
{
cookie = new HttpCookie(
_cookieConfig.getName(), // JSESSIONID
id,
_cookieConfig.getDomain(),
sessionPath,
_cookieConfig.getMaxAge(),
_cookieConfig.isHttpOnly(),
_cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure));
}
else
{
cookie = new HttpCookie(
_cookieConfig.getName(),
id,
_cookieConfig.getDomain(),
sessionPath,
_cookieConfig.getMaxAge(),
_cookieConfig.isHttpOnly(),
_cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure),
_sessionComment,
1);
}
return cookie;
}
return null;
}
不解释了,看我在代码中添加的注释应该就清楚了。
最后小本子整理笔记:
session 需要服务端新建后,将id放入cookie里发送到客户端,客户端下次请求放入头部传到服务器端来验证是否是同一会话。
如果不手动调用 session 的创建代码,浏览器端并没有显示 cookie,更看不到 JSESSIONID,浏览器不显示 cookie
最近比较忙,趁着周五,加个班肝了这一篇。
祝大家周末愉快~
如果觉得还不错的话,关注、分享、在看, 原创不易,且看且珍惜~