经典文章:
https://blog.csdn.net/tanyunlong_nice/article/details/47188659
https://mp.weixin.qq.com/s/NXrH7R8y2Dqxs9Ekm0u33w?
许令波:https://www.ibm.com/developerworks/cn/java/books/javaweb_xlb/10/index.html?mhq=%E8%AE%B8%20%E4%BB%A4%E6%B3%A2%20Cookie
cookie并不是某种语言特有的,它独立于语言存在,但是这些语言可以对cookie进行间接操作,即发送HTTP指令,浏览器收到指令便可创建cookie,若服务器设置了cookie有效期浏览器还会把cookie保存到客户端。在客户端Cookie一般由浏览器实现和管理的。
// org.apache.catalina.connector.Response @Override public void addCookie(final Cookie cookie) { // Ignore any call from an included servlet if (included || isCommitted()) { return; } // final StringBuffer sb = generateCookieString(cookie); addHeader("Set-Cookie", sb.toString()); } public StringBuffer generateCookieString(final Cookie cookie) { final StringBuffer sb = new StringBuffer(); //web application code can receive a IllegalArgumentException //from the appendCookieValue invocation // 仅当启用了安全性和包包裹机制被认为是cookie 0版本,否则为cookie 1版本 if (SecurityUtil.isPackageProtectionEnabled()) { AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run(){ ServerCookie.appendCookieValue (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(), cookie.getPath(), cookie.getDomain(), cookie.getComment(), cookie.getMaxAge(), cookie.getSecure(), cookie.isHttpOnly()); return null; } }); } else { ServerCookie.appendCookieValue (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(), cookie.getPath(), cookie.getDomain(), cookie.getComment(), cookie.getMaxAge(), cookie.getSecure(), cookie.isHttpOnly()); } return sb; }
可以看到,服务器端只是发送了一条Set-Cookie指令,将每一个cookie对象的信息添加到Header里,浏览器会读取然后创建(请参见Cookie笔记。),如果cookie设置了有效期,则保存在客户端本地磁盘。保存cookie的文件是一个文本文件,因此不用担心此文件中的内容会被执行而破坏客户的机器。创建cookie时如果不指定有效期,则cookie只在浏览器关闭前有效,cookie会在服务器端和客户端传输,但是不会保存在客户机的磁盘上,当你关闭了该浏览器则cookie失效。注意:如果是在浏览器端设置的cookie,关闭标签页cookie并不会失效。
chrome的cookie信息一般保存在C:UsersadminAppDataLocalGoogleChromeUser DataDefaultCache目录中,但是cookie文件打开是乱码的。
cookie是有大小限制的,如果设置了有效期将保存在客户端磁盘。
cookie一般由服务器端发送创建指令,浏览器端生成,再次访问服务器时,HTTP请求会携带着有效的cookie,将cookie传递到后台。
cookie不宜设置过大也不宜设置过多,在高并发访问环境下,cookie如果设置过大或过多,每次请求都会增加客户端和服务器的数据传输量(为防止这种情况发生一般使用session)。
使用session,同一个客户端和服务器交互时,不需要每次都传回所有的Cookie值,而只要传回一个ID,这个ID是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的ID,客户端只要传回这个ID就行了,这个ID通常是NAME为JSESIONID的一个cookie。
Session默认支持基于URL Path Parameter,当客户端不支持cookie时,浏览器会将用户的"JSESSIONID=****"重写到URL中,如果你web.xml中session-config下的cookie-config的name属性没配置的话那就默认显示的是"JSESSIONID",当然session中还可以用来存储其他的key和value,如果有的话就是:"/ProjectName/Servlet;JSESSIONID=***;key1=value1?id=10",注意看,在"Servlet"后以";"开始之后一直到"?"为止的位置内都是session存储的内容,"?"之后即为要传的参数。
如果客户端支持cookie,Tomcat仍然会解析cookie中的"JSESSIONID",并会覆盖URL中的"JSESSIONID"。
在Java中,当客户端第一次访问服务器时,服务器端通过request.getSession()为当前用户创建唯一的Session,如果不是第一次访问,也是通过request.geSession()去拿到session集合里的这个session。如果Servlet没有主动去创建也没有返回JSP,则不会生成session,若Servlet没有主动创建而是返回了一个JSP文件,由于session为JSP的内置对象,在JSP被编译成Servlet时是通过pageContext.getSession()去为当前用户创建唯一的Session,其实背后也就是使用request.getSession()。
上面说到了一个session集合,我们知道cookie是有生命周期的,cookie在客户端一般交给浏览器管理的,当浏览器检测到这个cookie失效后就会删除,那session在服务器端呢?也总要有谁去管理和维护吧?
从上图中我们可以清楚的看到,session被创建和管理的过程。但是第一次请求的时候,客户端的Path的Parameters和cookie里是没有Session ID的,并不像图上一开时那样,一开始没有Session ID的,那怎么办呢?没有Session ID简单啊,就直接跳过4,5直接request.getSession()不就行了。如果不是第一次请求了,而且后台也生成过Session ID了,那就走上图那样的步骤。
从图里面你也可以看到,session只是针对于后台来说的,前台只是cookie,所以说session是基于cookie来工作的。如果你了解cookie你会发现cookie和session具有很多相似性,一个是交由浏览器管理,一个是交由StandardManager管理,cookie可以通过document.cookie创建修改删除,session通过HttpSession修改删除。
StandardManager是怎么管理session的?浏览器是怎么管理cookie我是凭借着测试和感觉推测浏览器会定期检测cookie,过期的cookie就将其删除,没过期的某网站A的cookie可能在访问网站A时被放入Http头中,传到后台。我看先看一张图:
这张图我不知道作者为什么将Request的箭头指向Manager,不应该是Manager依赖Request吗》为什么会是Request依赖Manager?哦!我大概知道了,看到图1我就明白了,Request依赖Manager去管理其创建的session,而不是Manager依赖Request获取session并管理,虽然两个都可以实现session被Manager管理,但是主次关系不一样了,在交互场景中Request始终是主方。
StandardManager类负责Servlet容器中所以StandardSession对象的生命周期管理,当Servlet容器重启或者关闭时StandardManager负责持久化没有过期的StandardSession对象,它将所有的StandardSession对象持久化到一个"SESSIONS.ser"为文件名的文件中。
到Servlet容器重启时,也就是StandardManager初始化时,会重新读取这个文件解析出所有的Session对象,重新保存在StandardManager的sessions集合中。Session恢复状态图如图2
注意:要持久化保存Servlet容器中的Session对象,必须调用Servlet容器的stop,而不能直接kill Servlet容器进程,因为直接结束进程,Servlet容器没有机会调用unload方法来持久化这些session对象。
在Tomcat中session的有效期时60(maxInactiveInterval属性控制)秒,超过60秒该session过期,检测每个Session是否失效实在Tomcat的一个后台线程中完成的。
除了后台进程检测session是否失效外,调用request.getSession()时也会检测该Session是否过期。值得注意的是,request.getSession()方法调用的StandardSession用过都会存在,即使与这个客户端关联的Session对象已经过期。如果过期,又会重新创建一个全新的StandardSession对象,但是以前设置的session值将丢失。也就是说如果客户端间隔很长一段时间拿着在客户端没有过期的cookie,但是在服务器端已经过期的session,那之前的那个session对象已经被后台线程设置为失效,也就是说它将会被回收,但是服务器会拿着客户端传递过来的cookie创建一个新的session,旧的session被回收了。如果客户端很久不在访问,旧的session被情况不会新创建新的。是否创建新的取决于客户端是否再次访问。
如果你取到了的session对象,但是通过session.getAttribute取不到以前设置的值,你应该知道为什么,旧的过期被回收了,你现在获取的是新的,所以里面没有值。如果不想让session过期可以设置maxInactiveInterval属性为-1,设置-1带来的危险就是当不同的访问到来你服务器的session会越来越多,如果是固定量的用户访问还好说,你可以使用request.getSession(boolean create)方法判断一下该客户端关联的session对象是否存在,create为true就代表如果不存在就新建返回一个新的session,为false不存在不新建返回null。如果不固定且用户量大,这样一直存在你的容器中你就完蛋了!等死吧你!
cookie的生命周期是累计的,从创建时计时,10分钟后,cookie生命周期结束。
session的生命周期是间隔的,从创建时计时,如果在10分钟内没有访问session,那么session的生命周期结束.如果访问过session,session从访问的时间开始从新计算时间(从0开始到10)。
网站中很多地方都有表单重复提交问题,一种情况下是用户在网速慢的情况下可能会重复提交表单,还有就是恶意用户通过程序来发送恶意请求,在这种情况下都要设计一个防止表单重复提交的机制。
要能够防止表单重复提交,就要标识用户的每一次访问请求,使得每一次访问对服务器来说都是唯一确定的。
<form id=”form” method=”post”> <input type=hidden name=“crsf_token” value=“xxxx”/> </form>
在服务器端,生成一个唯一的标识符,将它存入session,同时将它写入表单的隐藏字段中,然后将表单页面发给浏览器,用户录入信息后点击提交,在服务器端,获取表单中隐藏字段的值,与session中的唯一标识符比较,相等说明是首次提交,就处理本次请求,然后将session中的唯一标识符移除;不相等说明是重复提交,就不再处理。
上图是用户发起对表单页面的请求过程,生成唯一的 token 需要一个算法,最简单的就是可以根据一个种子作为 key 生成一个随机数,并保存在 Session 中,等下次用户提交表单时做验证。验证表单的过程下图所示。