在Session篇说过为了维持Http请求有状态化,需要有“会话”的概念,服务端信息+客户端信息共同协作维持会话。
其中Cookie是会话在客户端的载体,Session是会话在服务器端的载体。客户端会话存储在浏览器中。服务器端会话存储在Web中间件中。
单论Cookie的产生,网上查是早于Session,当时只是想在浏览器端保存一些网站的设置信息,所以起了这么个名字。理论上登录才是第一需要解决的问题。。。
Cookie的内容由服务器端产生,通过构建Cookie对象,调用Response的setCookie方法设置。或者直接通过Response的addHeader方法来设置,通过Response Header传递给浏览器端。
Cookie cookie = new Cookie("test", "11");
cookie.setPath("/");
cookie.setHttpOnly(true);
response.addCookie(cookie);
// response.addHeader("Set-Cookie", "uid=1123456; Path=/; HttpOnly");
Cookie也可以在前台通过js设置,一般用于后台不方便设置的时候,如我们在ajax访问中希望在返回结果后增加Cookie。
document.cookie="name="+value;
Cookie主要属性是一个键值对name和value,还有其它的附属控制属性。
我们可以查看Java中Cookie的源码
private String name;
private String value;
private String comment;
private String domain;
private int maxAge = -1;
private String path;
private boolean secure;
private int version = 0;
private boolean isHttpOnly = false;
public Cookie(String name, String value) {
// 构造函数逻辑
}
属性说明:
跨域的一种情况
假设某个公司有两个站点,siteA.abc.com,siteB.abc.com,如果想要相互访问,如siteA中有个Cookie希望能被siteB访问到,无法显式将site设为siteB.abc.com。
可以都将domain设置为父级域名,都设置为abc.com。这在使用单点登录时作为一种简单的处理跨域请求的方法。
domain和path的默认值设置逻辑,在java.net包的CookieManager.java下,描述了如何按照RFC 2965文档标准进行的值设置逻辑。
但是我debug并不会走这里,也没在java代码中找到哪里设置了这俩的默认值,我甚至怀疑这个值是浏览器最终判定与设定的。。。如果有人找到设置默认值的地方,请告诉我。
maxAge
默认值为-1
表示Cookie的过期时间,如果Cookie设置了过期时间,Cookie会存储在硬盘中。如果设置Cookie为-1(默认值)或者0,Cookie会在浏览会话结束后删除。
浏览会话结束的意思是浏览器完全关闭
不是页面关闭,即使将当前网站的所有页面关掉,设置为maxAge -1的Cookie仍然存活。
假设浏览器能开启多个,需要浏览器完全关闭,多个只是表现形式而已,事实上只有一个实例开启着。
version
默认值为0。
设置Cookie的版本,Cookie Version 0版本中,空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号等“特殊”字符都不能作为Cookie的value。新的Cookie Version 1的版本可以使用这些字符,但是尚未被所有浏览器支持,主要是这个改变貌似。。。并没有多么必要。
secure
默认值为false
如果设置为true,表示只有https的请求才会去查询这个cookie,否则http和https都可以访问到cookie。如果你修改了请求的方式,可能cookie就查询不到了。
Cookie存储的时候,相同domain内,会按照协议分成两个大组, http和https,以https://开头的请求,只会取https组里面的Cookie。
我们会在CA篇中详细讲到https。
httpOnly
默认值为false
如果设置为true,意味着只能从ServletRequest中获取Cookie,无法通过JavaScript获取Cookie,是防止XSS攻击的一种手段。
Cookie存储在浏览器端,不同的浏览器存储位置不一样 ,无法跨浏览器使用。
浏览器会先按照domain分组,domain内部按协议分组,组内按path分树的方式保存Cookie。这样最方便查询。
在chrome浏览器中,可以通过设置(chrome://settings/)->高级->内容设置->Cookie->查看所有 Cookie 和网站数据来查看和单独删除某个网站的Cookie。
这里面脚本可访问就是httpOnly,到期时间就是maxAge
在清除浏览器缓存时,如果选择了清除Cookie,那么将会清除掉保存的网站设置,网站账号密码,和当前登录有关的数据JSESSION_ID、JWT、或者其它Token
浏览器发送任何请求的时候,会根据当前请求的domain和path去cookie里面匹配,将匹配到的cookie带在Request Header中去请求数据。
后台通过HttpServletRequest的getCookies()方法获取Cookie[]数组,我们需要通过getName来进行匹配,找到我们想用的Cookie。
传到后台的Cookie中domain和path都为空,只要是浏览器自动带着的,就是浏览器根据当前request的url筛选后的,我们直接使用即可,domain和path没有意义。
Cookie存储有大小与数量的限制,不同浏览器实现不同,大约是每个域名下4k的空间与50个Cookie。
Web Storage由于和Cookie的可比性很强,并且内容很简单,我们放到一起说下。
Cookie的特性,导致了另外一种(两种)Web端存储的产生。
为了解决Web端存储的问题,H5推出了Web Storage。
有两种Web Storage,LocalStorage和SessionStorage。这两种的唯一区别就是SessionStorage会在浏览器会话结束后清空,这和maxAge为-1的Cookie效果相同。而LocalStorage永久存储,只能通过编程手段删除(当然这并不绝对,并不能阻止你找到LocalStorage的本地存储位置,去删掉)。
Web Storage通过键值对保存,键值都为String类型,如果你想在键/值中存储复杂对象,需要使用JSON.stringify或者JSON.parse进行序列化与反序列化。
Web Storage通过同源策略访问,这一点和Cookie差别比较大,Cookie依靠domain和path,如果不限制secure,将能取到http和https的。Web Storage依靠同源,也就是协议+domain+端口。
Web Storage能够存储5M的数据,并不限制数据条数,足够保存很多应用的离线数据。
Web Storage被设计为Web端的存储介质,作为存储比Cookie有很多优势。但是毕竟只是Web端存储,数据安全性无法保证。这包括两个方面:1、数据并不是绝对不会被删除;2、数据以明文显示,很好获取。
所以WebStorage只是作为辅助存储的手段,应用在如离线数据等非敏感数据的本地缓存。
我们会在Jwt篇横向比较Jwt Token在Web端的存储方式,LocalStorage配合Authorization Header,会是保存Jwt Token的最佳实践。