文章的背景是:浏览器的Cookie未被禁用
.
我们知道,无论是session还是cookie都是在服务端创建的,只是cookie不同于session被保存在客户端。
工作流程如下:
- 客户端第一次请求服务器,cookie中不存在与url相对应的sessionid。所以request headers 的cookie头里不包含sessionid的内容,甚至不包含cookie请求头;
- 服务端调用 request.getSession(); ,没有sessionid自然无法取得相应的session信息,立即创建新的session;
- 新创建的session信息被服务端存储下来,sessionid被添加到response的set-cookie响应头,并返回给客户端;
- 客户端接收到response报文的cookie内容,并将其保存在本地;
- 客户端再一次请求服务器,浏览器将根据url,解析出相应的sessionid,并添加到cookie请求头,发送给服务器;
- 服务端调用 request.getSession(); ,根据sessionid从session存储里获得相应的session信息,然后进一步操作,再响应给客户端。
事实上cookie没那么神秘,它被抽象为一个实体类,包含着一系列属性和方法,如下:
new Cookie(String name,String value) //创建cookie
response.addCookie(Cookie cookie) //向response中添加cookie,可以多次调用以添加多个cookie
getName() //获得cookie的name
getValue() //获得cookie的value
setValue(String newValue) //用于修改name对应的value值。
setMaxAge(int expiry) //设置有效时间,单位s,缺省-1,即关闭浏览器cookie失效
setPath(String uri) //设置路径
setDomain(String pattern) //设置域名,(一般浏览器会自动设置,服务端的操作无效)
setDomain(".zyh.com") //如果这样设置,www.zyh.com / bbs.zyh.com 都可以访问,a.b.zyh.com无法访问
isHttpOnly() //是否只能在服务端操作cookie,而客户端javascript不能。不是cookie规范,而是浏览器支持
setComment(String purpose) //cookie的描述信息
setSecure(boolean flag) //是否使用安全传输协议。为true时,只有用https请求时cookie才会被发
//送给服务器端,而http时不会。但是客户端还是可以接收来自服务端的cookie。缺省 false
setVersion(int v) //编译规范,默认0
对于客户端而言,cookie
仅仅是一个对象实例
,包含着key,value,domain,path等一系列附加信息,序列化后被保存在客户端。而客户端可能同时保存着成百上千个这样的对象实例,确切的说是更像是**cookie集合
**,类似于:List<Cookie>。
那么浏览器到底如何保证从cookie中解析并发送对应的sessionId到目标Url呢?
- 要理解浏览器如何解析出sessionid,首先要知道包含sessionid的cookie是如何被创建的:
Cookie sessionCookie = new Cookie("sessionid","1234");
sessionCookie.setPath("/path"); //可以在服务端设置
sessionCookie.setDomain("www.test.com"); //一般情况下domain被浏览器重新设置,服务端的设置无效
response.addCookie(sessionCookie);
-
List<Cookie>被序列化存储在客户端,我们可以将它理解成一个数据表,表结构如下:
name|value|domain|path|other_info
-|
| -
浏览器通过URL来访问服务端,URL可能是下面三种样式。在此之前要知道,无论是**
域名
,域名+端口
还是ip地址+端口
,对于Http*协议来说都是Domain**。
www.test.com/path/.. # 域名+路径
www.test.com:8080/path/.. # 域名+端口+路径,当然这种写法不常见,太丑了。
#通常在服务端域名配置的时候应该已经做好了指定端口的绑定
127.0.0.1:8080/path/.. # ip地址+端口+路径,当然这种写法也只会在开发环境下存在
-
为了方便理解,按照我们假设的存储方式,三种Url对应的cookie被存储在cookies数据表中:
name|value|domain|path|other_info
-|
非sessionid|非sessionvalue|www.test.com:8080|path|其他信息
非sessionid|非sessionvalue|127.0.0.1:8080|path|其他信息
非sessionid|非sessionvalue|www.test.com|path|其他信息
sessionid|1234|www.test.com|path|其他信息 -
同理,浏览器从cookie中解析出目标URL对应sessionid的过程,我们可以简单理解为对数据表的查询。假设,我们访问 url:
http://www.test.com/path/../..
, select 语句如下:
select name , value
from cookies
where domain='www.test.com' and path='path'
-
查询结果如下:
name|value|domain|path|other_info
-|
非sessionid|非sessionvalue|www.test.com|path|其他信息
sessionid|1234|www.test.com|path|其他信息 -
我们很清楚,只有
name = 'sessionid'
的那条cookie才是我们想要的 存储sessionid的cookie,这步筛选过程,当然 只会在服务端发生。此时request的请求头内容如下:
Request Headers
Cookie:sessionid=1234; 非sessionid=非sessionid
注意
- 上述例子中,cookie中包含中文信息,实际使用,中文信息需要编码/解码。
//发送cookie
Cookie cookie = new Cookie(URLEncoder.encode("非sessionid")
,URLEncoder.encode("非sessionvalue"));
cookie.setComment(URLEncoder.encode("其他信息"));
response.addCookie(cookie);
//获得cookie中文内容
URLDecoder.decoder(request.getCookie().getName);//获取name
URLDecoder.decoder(request.getCookie().getValue);//获取value
强调
Cookie 的
Domain
与Path
属性,是根据url解析出对应cookie的**充分必要条件
**
- setDomain(String urlPattern); //尽管服务端开放了操作Domain属性的方法,但是一般情况下,浏览器会自动重写该属性,所以服务端的设置通常是无效的。
- setPath(String path); //服务端可以对Path进行设置,不会被浏览器复写,通常有效。
- 手动设置
Cookie sessionCookie = new Cookie("sessionid","1234");
sessionCookie.setPath("/path");
- 传统Spring项目,修改tomcat的server.xml的**
<Context path="" />
**
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
<Context docBase="kuaiban-platform-service" path="/kuaiban-platform-service" />
<!--这里path 即context-path ,指定了访问Web应用的Url入口-->
</Host>
- Spring Boot,可以直接配置**
application.properties
/application.yml
**
server.session.cookie.path= # Path of the session cookie. default "/"