Cookie和Session解析
1 Cookie工作原理
会话是由一组请求和响应组成,在请求和响应之间一定是需要数据传递的,即需要进行会话状态跟踪。(会话可以理解为,打开浏览器,从发出第一次请求开始,到最后关闭浏览器,为一次会话。)但是HTTP协议是一种无状态协议,在不同的请求之间无法进行数据传递和共享,也就是说,这一次请求和上一次请求是没有任何关系的,互相不认识。那么,就需要Cookie来实现不同请求之间数据传递的会话跟踪
Cookie是由服务器生成的,保存在客户端的信息载体,维系客户端和服务器之间的状态同步。HTTP最初的设计是无状态的,但是无状态的HTTP无法满足日益发展的需求,所以业界扩展了HTTP协议,增加了有状态的协议头,使之变成有状态协议。而这个有状态的协议头,就是靠Cookie实现的
当用户在提交第一次请求时,由服务器生成Cookie,并将其封装到响应头中,以响应的形式发送给客户端。客户端收到这个响应后,将Cookie保存到客户端。当客户端再次发送同类请求,在请求中会携带保存在客户端的Cookie数据,发送到服务端,由服务器对会话进行跟踪
2 Cookie作用范围
2.1 默认情况
默认情况下,Cookie只有在同类请求下才有效。所谓同类请求,就是指资源路径相同的请求。只有资源路径相同的请求,才会带上Cookie
比如,以下服务器代码会设置一个Cookie
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/test")
public String hello(HttpServletResponse response) {
Cookie cookie = new Cookie("test", "hello");
response.addCookie(cookie);
return "success";
}
}
那么,在第一次访问localhost:8080/hello/test
这个路径的时候,我们可以看到
在响应头中有Set-Cookie
项,并且Cookie里面保存的键是test,值是hello
当我们访问localhost:8080/hello/test1
这个路径的时候,虽然会返回404,但是我们在请求头中可以看到存在Cookie
说明,这次请求是带了上次返回来保存的Cookie的
但是,当我们访问localhost:8080
或者localhost:8080/test
等其他不是localhost:8080/hello
下的资源路径,在请求头中不存在Cookie
说明,这次请求不带Cookie
以上是默认情况下,同一个Cookie的作用范围,也就是在同类请求才会带上Cookie
2.2 设置Cookie作用路径
我们可以手动设置Cookie的作用路径
比如
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/test")
public String hello(HttpServletResponse response) {
Cookie cookie = new Cookie("test", "hello");
cookie.setPath(request.getContextPath() + "/abc");
response.addCookie(cookie);
return "success";
}
}
我们关闭之前的浏览器,重新打开(或者直接清除之前保存的Cookie),访问localhost:8080/hello/test
,可以看到
同样在响应头中返回了一个Set-Cookie
,但是在里面还附加了一个Path,指定了此Cookie的作用范围
当我们访问localhost:8080/hello/test1
时,在请求头中看到,没有Cookie项
但是,但我们访问localhost:8080/abc
或者/abc路径下的资源时,在请求头中存在Cookie项
以上便是手动指定Cookie的作用范围
3 Cookie生命周期
默认情况下,Cookie的生命周期为浏览器会话期间,Cookie保存在浏览器缓存中,只要关闭浏览器,Cookie就会消失
但是,可以手动设置Cookie的有效时间。此时,Cookie会被保存在硬盘中,在有效期内,关闭浏览器,Cookie并不会消失
4 服务端的Cookie解析
根据上文讲的Cookie的作用范围和生命周期,我们可以编写服务端的Cookie解析
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/test")
public String hello(HttpServletRequest request, HttpServletResponse response) {
Cookie cookie = new Cookie("test", "hello");
response.addCookie(cookie);
return "success";
}
@RequestMapping("/parse")
public String parse(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
String res = "";
for (Cookie cookie : cookies) {
res += cookie.getName() + ": " + cookie.getValue();
}
return res;
}
}
获取到Cookie之后,就可以利用Cookie实现一些功能
5 Session工作原理
Session,也就是会话,是Web开发中的一种会话状态跟踪技术。作用和Cookie是类似的,只是Cookie由服务端生成,保存在客户端,而Session则是保存在服务端
在服务器中会为每个会话维护一个Session,不同的会话对应不同的Session。也就是说,服务端会同时存在多个Session,以供不同的会话使用。那么为了达到不同的会话使用不同的Session,Session是配合Cookie实现的
1. 写入Session列表
服务器对当前的Session是以Map形式进行存储的,这个Map被称为Session列表。这个Map的key是一个32位长度的随机串,称为JSessionID
,value则是Session对象的引用
当用户第一次提交请求的时候,服务端执行到request.getSession()
方法后,会自动生成一个Map.Entry
对象,key为一个根据某种算法生成的JSessionID,value则为新创建的HttpSession对象的引用
2. 服务器生成并发送Cookie
在将Session的信息写入Session列表后,系统还会将"JSessionID"
作为name,JSessionID的值
(32位长度的随机串)作为value,以Cookie的形式存放到响应头中,发送到客户端
@RequestMapping("/test2")
public String setSession(HttpSession session) {
session.setAttribute("hello", "world");
return "seccess";
}
在请求这个方法后,我们可以看到
在响应头中,有一个Set-Cookie
项,里面是JSessionID和对应的值
3. 客户端接收并发送Cookie
客户端接收到这个Cookie之后,会将其存放在浏览器的缓存中,也就是只要浏览器不关闭,缓存中的Cookie不会消失。当用户提交第二次请求时,会将缓存中的这个Cookie放入请求头中,发送到服务端
比如
@RequestMapping("/test3")
public String getSession(HttpSession session) {
String param = (String) session.getAttribute("hello");
return param;
}
当访问此代码时,可以看到
在请求头中的Cookie项中,有JSessionID,而且在后端也成功获取到了Session中"hello"对应的值"world"
4. 从Session列表中查找
服务器从请求中读取到客户端发送来的Cookie,根据Cookie的JSessionID
值,从Map中获取对应的HttpSession对象的引用,然后可以对该对象的域属性进行读写操作
5 Session的生命周期
Session是有存活时间的,而Session失效就是指Session的超时,如果某个Session在指定的时间内一直未被访问,那么该Session将超时,会失效
默认的Session超时时间为30分钟,可以手动设置超时时间,也可以手动提前使Session失效(Session失效以后,对应的引用并不为空,但无法再继续使用,内部所有键值对全部解绑)
6 总结
由于HTTP协议的无状态性,为了使某个域名下的所有网页能够共享某些数据,我们可以使用Cookie和Session
Session存储在服务器,为一个Map,拥有唯一识别符号JSessionID,通过设置Cookie发送给客户端,Cookie存储在客户端中
客户端携带Cookie发送请求到服务器,服务器从Cookie中解析出JSessionID,再从Map中找到对应的Session
Cookie是实现Session的其中一种方案,也是最常用的,但不是唯一方法,禁用Cookie之后还有其他方法存储,比如放在url中