1.会话技术
1.1 为什么需要会话技术
想必大家都有这样的经验:
- 登录京东,选了iphone x放入购物车后关闭浏览器。再次打开时,发现又要重新登录。
- 登录淘宝,在一个页面逗留了很久,终于决定要买了,却提示你重新登录。
这一切,好像是服务器在“监视”着浏览器的行为,服务器会想:
- 咦?浏览器被关了,应该是不买了,那我把登录状态给关闭。
- 这个页面30分钟没动静,用户可能离开了,为了防止路过的人使用该账号,那我把登录状态给关闭得了。
但我们只是服务器与客户端的请求是HTTP协议,表示一种无状态协议,也就是服务器不可能知道客户端发生了什么,服务器只是被动响应客户端请求, 也不可能监控客户端到底做了啥?
既然服务器并不知道客户端做了啥, 那以上两种情况,是如何做到的呢?
答案: 会话跟踪
1.2 会话概述
会话一词,指聚谈、对话,最早出现在欧阳修《与吴正肃公书》一文中:"前约临行少留会话,终不克遂,至今为恨。"基本可以理解为就是一次对话
计算机术语:指客户端向服务器发送的多次请求,在某一时间段内都属于同一个会话。
举个栗子:
假设我们注册了一个论坛,因为HTTP是无状态的,服务器无法辨认访问者,那么即使是同一个网站的不同页面,服务器都必须强制访问者重新登录一次,以确定你是合法用户,所以会话机制显得很有必要,由于某些原因Http必须是无状态的,所以会话跟踪技术就是对HTTP无状态协议的一种扩展。
从以上图中可以看出,服务器可通过会话跟踪技术识别客户端用户是”小A“或”小B“ ,其实服务器在第一次就收到”小A“的请求时,并不知道它是谁, 通过给客户端分配唯一表示(序列号),服务器同步保留该标识,等下次”小A“再次访问时,服务器先检查”小A“是否有唯一序列号,从而能识别它的访问状态。
这里只是简单阐述身份识别的过程,后面在session的原理中,再详细给大家描述。
2. Cookie
2.1 Cookie的概念
在Java的会话跟踪技术中最常用的两种技术:Cooke和Session,并且Session底层也是依赖于Cookie的。
Cookie是一种客户端的会话技术,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
2.2 Cookie常用API
- 创建一个Cookie对象(cookie只能保存字符串数据。且不能保存中文)
new Cookie(String name,String value);
- 把cookie写回浏览器
response.addCookie(cookie);
- 获得浏览器带过来的所有Cookie:
request.getCookies() ; //得到所有的cookie对象。是一个数组,开发中根据key得到目标cookie
- cookie的 API
cookie.getName() ; //返回cookie中设置的key
cookie.getValue(); //返回cookie中设置的value
2.3 Cookie的应用
2.3.1 服务器如何发送Cookie
a.Servlet代码
/**
* @ClassNAME SendCookieServlet
* @Description
* @Author atguigu
* @Date 2021/10/9 14:28
* @Version 1.0
**/
@WebServlet(urlPatterns = "/sendCookieServlet")
public class SendCookieServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.创建Cookie对象,并设置cookie的key和value
Cookie cookie1 = new Cookie("name","atguigu");
Cookie cookie2 = new Cookie("time","6pm");
Cookie cookie3 = new Cookie("table","69");
//2.将Cookie对象保存到响应对象中,并发送给客户端
resp.addCookie(cookie1);
resp.addCookie(cookie2);
resp.addCookie(cookie3);
}
}
b.启动服务器,并访问如下地址
localhost:8080/CookieTest/sendCookieServlet
Tomcat在给浏览器发送HTTP响应前,会从response对象中取出HTTP相关的信息(比如响应体中的HTML,响应头),其中就包括Cookie
响应头中其中一个Set-Cookie中存储的就是我们写入的数据 (name、time、table)
Set-Cookie:name=atguigu
Set-Cookie:time=6pm
Set-Cookie:table=69
浏览器接收到这些响应头后,会把它们作为Cookie文件存在客户端,当我们第二次请求同一个服务器时,浏览器会在请求头中带上这些Cookie值 ,如图:
示意图:
2.3.2 服务器如何获取Cookie
通过以上案例,客户端已经有cookie了,由于没有设置Cookie的过期时间,只要该浏览器不关闭,这个cookie将一直生效,直到关闭浏览器为止, 只要在此期间发送任何请求,服务器都能获取该Cookie的信息,那么此时我们应该如何获取客户端存放的Cookie数据呢? 请看下面
/**
* @ClassNAME GetCookieServlet
* @Description
* @Author atguigu
* @Date 2021/10/9 15:11
* @Version 1.0
**/
@WebServlet(urlPatterns = "/getCookieServlet")
public class GetCookieServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取客户都安携带的cookie的数据
Cookie[] cookies = request.getCookies();
if (cookies != null) {
//2.通过cookie名称获取想要的cookie
for (Cookie cookie : cookies) {
//3.获取cookie的名称
String name = cookie.getName();
if ( "name".equals(name) || "time".equals(name)
|| "table".equals(name)){
String cookieValue = cookie.getValue();
//4.打印从客户端获取的cookie值
System.out.println(name + "的值:" + cookieValue);
}
}
}
}
}
注意:虽然请求头中cookie的内容是name=atguigu;time=6pm;table=69,但并不需要我们在Servlet获取时进行切割,只需要调用request.getCookies()即可得到cookie数组,里面包含浏览器存储的所有cookie,我们只需要获取自己存储的即可。
简图如下:
但是,上面SendCookieServlet的代码是有个问题的!
此种方式传给客户端的Cookie会随着客户端(浏览器)的关闭而消失,这也就是为什么你登录京东,添加商品进购物车,关闭浏览器重新打开时却要你再次登录的原因。
因为第一次访问京东登录后,京东服务器给你浏览器一个Cookie,当你关闭浏览器时,Cookie消失,下次你再打开浏览器,服务器找不到证明你登录过的Cookie标识,所以让你重新登录。
2.4 Cookie的时效性
如果我们不设置Cookie的时效性,默认情况下Cookie的有效期是一次会话范围内,我们可以通过cookie的setMaxAge()方法让Cookie持久化保存到浏览器上
- 会话级Cookie
- 服务器端并没有明确指定Cookie的存在时间
- 在浏览器端,Cookie数据存在于内存中
- 只要浏览器还开着,Cookie数据就一直都在
- 浏览器关闭,内存中的Cookie数据就会被释放
- 持久化Cookie
- 服务器端明确设置了Cookie的存在时间
- 在浏览器端,Cookie数据会被保存到硬盘上
- Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响
- 持久化Cookie到达了预设的时间会被释放
cookie.setMaxAge(int expiry)
参数单位是秒,表示cookie的持久化时间,如果设置参数为0,表示将浏览器中保存的该cookie删除
更改以上代码:
/**
* @ClassNAME SendCookieServlet
* @Description
* @Author atguigu
* @Date 2021/10/9 14:28
* @Version 1.0
**/
@WebServlet(urlPatterns = "/sendCookieServlet")
public class SendCookieServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.创建Cookie对象,并设置cookie的key和value
Cookie cookie = new Cookie("name","atguigu");
//2.为Cookie设置持久化时间,10分钟
cookie.setMaxAge(10*60);
//3.将Cookie对象保存到响应对象中,并发送给客户端
resp.addCookie(cookie);
}
}
重启服务器,访问localhost:8080/CookieTest/sendCookieServlet
小结:
不设置MaxAge,默认响应会话Cookie(MaxAge<0),存在浏览器内存中,浏览器关闭Cookie消失
设置MaxAge>0,响应持久化Cookie,存在电脑硬盘的特定文件夹下(浏览器自定义的)
-
设置特定Cookie的MaxAge=0,则会删除已经存在客户端的此Cookie(我们可以通过此方法删除某个Cookie)
一般,响应给客户端的Cookie都是会话Cookie(不设置MaxAge),是存在浏览器内存中,所以关闭浏览器Cookie消失,则下次请求服务器时,请求头中不存在代表用户信息的Cookie(唯一标识用户),那么服务器就无法识别该用户了。
根据上面我们反向推到的结论:当服务器无法断定客户端时,一次会话就结束了
注意:对于不同浏览器,不管是会话Cookie还是持久化Cookie,都是相对独立的,所以当你用谷歌浏览器登录京东,再用IE打开京东依然需要重新登录,因为IE无法拿到谷歌存储的用户唯一标识的Cookie。
3. Session
3.1 Session概念
有了Cookie,似乎已经能解决问题,为什么还需要session呢?原因有很多,我们知道Cookie中存储的都是字符串类型的数据,那如果存对象怎么办呢?其次Cookie的数据始终保持在客户端,对于敏感信息也是不安全的,所以web项目必须建立存储安全可靠,且存对象,且数据量较大的内存模型,此时自然而然session这个重要的技术就这样诞生了。
session是服务器端的技术。服务器为每一个浏览器开辟一块内存空间,其本质就是要给大的Map。一般情况下,服务器会在一定时间内(默认30分钟)保存这个 Session,过了时间限制,就会销毁这个Session。在销毁之前,程序员可以将用户的一些数据以Key和Value的形式暂时存放在这个 Session中。
如上图所示,我们不再把“name=atguigu;time=6pm;table=69”作为Cookie放在请求头和响应头中,而只给客户端传递一个JSESSIONID=E3D7.... 唯一序列号,此时真正的用户数据存在服务器端的session对象中,当再次访问网站时, 会自动将JSESSIONID作为请求头数据“带回”给服务器,服务器可根据session中对应的JSESSIONID来找到用户数据。从而确保该用户的会话通信。
3.2 Session常用API
方 法 名 | 描 述 |
---|---|
HttpSession request.getSession() | 返回HttpSession类型的session(如果第一次调用其实是创建session对象,第一次之后通过sessionId找到session进行使用) |
void setAttribute(String attribute, Object value) | 设置Session属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大 |
String getAttribute(String attribute) | 根据字符串key获取存储在当前session中的value对象 |
Enumeration getAttributeNames() | 返回Session中存在的属性名 |
void removeAttribute(String attribute) | 移除当前session中对应key的值 |
String getId() | 获取该session的ID,该ID由服务器自动创建,不会重复。 |
long getCreationTime() | 返回Session的创建日期。返回类型为long,常被转化为Date类型,例如:Date createTime = new Date(session.get CreationTime()) |
long getLastAccessedTime() | 返回Session的最后活跃时间。返回类型为long |
int getMaxInactiveInterval() | 返回Session的超时时间。单位为秒。超过该时间没有访问,服务器认为该Session失效 |
void setMaxInactiveInterval(int second) | 设置Session的超时时间。单位为秒 |
void putValue(String attribute, Object value) | 不推荐的方法。已经被setAttribute(String attribute, Object Value)替代 |
Object getValue(String attribute) | 不被推荐的方法。已经被getAttribute(String attr)替代 |
boolean isNew() | 返回boolean ,判断是否为新会话,当第一次创建时返回true,不是第一次创建返回false |
void invalidate() | 使该Session失效 |
3.3 Session的工作机制
前提:浏览器正常访问服务器
- 服务器端没调用request.getSession()方法:什么都不会发生
- 服务器端调用了request.getSession()方法
- 服务器端检查当前请求中是否携带了JSESSIONID的Cookie
- 有:根据JSESSIONID在服务器端查找对应的HttpSession对象
- 能找到:将找到的HttpSession对象作为request.getSession()方法的返回值返回
- 找不到:服务器端新建一个HttpSession对象作为request.getSession()方法的返回值返回
- 无:服务器端新建一个HttpSession对象作为request.getSession()方法的返回值返回
代码验证
- 有:根据JSESSIONID在服务器端查找对应的HttpSession对象
- 服务器端检查当前请求中是否携带了JSESSIONID的Cookie
// 1.调用request对象的方法尝试获取HttpSession对象
HttpSession session = request.getSession();
// 2.调用HttpSession对象的isNew()方法
boolean wetherNew = session.isNew();
// 3.打印HttpSession对象是否为新对象
System.out.println("wetherNew = " + (wetherNew?"HttpSession对象是新的":"HttpSession对象是旧的"));
// 4.调用HttpSession对象的getId()方法
String id = session.getId();
// 5.打印JSESSIONID的值
System.out.println("JSESSIONID = " + id);
3.4 Cookie与Session的区别
1、cookie数据存放在客户的浏览器上,session数据放在服务器上.
简单的说,当你登录一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上面,默认情况下session的实现机制依赖cookie。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
3、设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。
4、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。
5、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,
其中可以保存更为复杂的数据类型)
4. Cookie与Session的应用场景
Cookie的典型应用:
(一):判断用户是否登陆过网站,以便下次登录时能够直接登录。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
(二):另一个重要的应用是“购物车”中类的处理和设计。用户可能在一段时间内在同一家网站的不同页面选择不同的商品,可以将这些信息都写入cookie,在最后付款时从cookie中提取这些信息,当然这里面有了安全和性能问题需要我们考虑了。
Session的典型应用:
Session的存储更为广泛,所有的对象均可使用Session存储,通过模板引擎技术(如Thymeleaf)可自动获取服务器端session的数据,大大提高了系统的灵活性。
站在会话的角度,我们可以将Cookie和Session视为同一类,只是他们在应用中都有自己的侧重点,一旦客户端禁用Cookie,那么Session也会失效,服务器也可以通过URL重写的方式来传递SessionID的值,因此不是完全依赖Cookie。如果客户端Cookie禁用,则服务器可以自动通过重写URL的方式来保存Session的值,并且这个过程对程序员透明。