一、会话技术
- 1.会话:一次会话中包含多个请求和响应,由于HTTP协议是无响应的,所有每次请求都是独立的,无法共享数据。会话技术就是为了解决这个问题的。举个简单的例子,我们在淘宝购物,每一次点击一个商品加入购物车,我们选择了多个商品,每一次点击,都是一次独立的请求操作,然后最后我们去购物车付款,这也是一次独立的请求,但是我们可以在购物车页面看到我们刚才添加的商品,这就是利用会话技术实现的。
- 2.一次会话指的是浏览器第一次给服务器资源发生请求,会话建立,直到有一方断开为止。
- 3会话技术就是在
一次会话的范围内的多次请求数据
之间来共享数据 - 4.方式
- 1.客户端会话技术:
Cookie
,就是把共享数据保存在客户端 - 2.服务端会话技术:
Session
,就是把共享数据保存在服务器
- 1.客户端会话技术:
二、客户端会话技术Cookie
2.1Cookie的快速入门
- 使用步骤
- 1.创建Cookie对象,绑定数据:
new Cookie(String name, String Value)
- 2.发送Cookie对象:
response.addCookie(Cookie cookie)
- 3.获取Cookie对象,拿到数据:
Cookie[] request.getCookies()
- 1.创建Cookie对象,绑定数据:
- 实验:准备两个Servlet一个发送Cookie,一个获取Cookie
@WebServlet("/cookieDemo1")
public class CookieDemo1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建Cookie对象
Cookie c = new Cookie("msg", "hello");
//2.发送Cookie
response.addCookie(c);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
@WebServlet("/cookieDemo2")
public class CookieDemo2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//3.获取Cookie
// 因为可能获取多个嘛 所以有数组
Cookie[] cs = request.getCookies();
//遍历
if (cs != null){
for (Cookie cookie : cs) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.println(name + ":" + value);
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
- 注意:
- 必须先发送才能获取
- 如果用谷歌浏览器访问Demo1发送Cookie,再用其他浏览器去访问Demo2获取Cookie,是获取不到的
2.2原理分析
- 浏览器访问
/cookieDemo1
时,response
会自动的在响应头
里设置Cookie
的相关信息,可以通过抓包观察到
- 浏览器访问
/cookieDemo2
的时候,会在请求头里添加刚才的收到的Cookie
数据,也可以很容易的在抓包工具里观察到:
- 整个过程可以用下图表示:
- 小结:
基于响应头set-cookie
和请求头cookie
实现
2.3Cookie的细节
2.3.1一次可不可以发送多个Cookie?
- 可以
- 直接设置多个Cookie即可,在响应头里多个键值对保存即可
//1.创建Cookie对象
Cookie c1 = new Cookie("msg", "hello");
Cookie c2 = new Cookie("name", "zqx");
//2.发送Cookie
response.addCookie(c1);
response.addCookie(c2);
2.3.2cookie在浏览器存活多长时间?
- 默认情况下,cookie存放在浏览器的内存中,当浏览器被关闭,cookie数据被销毁
- 可以设置cookie信息持久化存储:
setMaxAge(int seconds)
- 正数:将cookie数据写入硬盘中,持久化存储,并指定cookie存活时间,时间到后,文件自动失效
- 零:删除cookie信息
- 负数:就是默认情况
//1.创建Cookie对象
Cookie c1 = new Cookie("msg", "hello");
//2.设置cookie的存活时间
//2.1持久化到硬盘 30s后失效
// c1.setMaxAge(30);
//2.2 设置-1 就是默认请教
// c1.setMaxAge(-1);
//2.3设置0 删除cookie信息
c1.setMaxAge(0);
//2.发送Cookie
response.addCookie(c1);
2.3.3 cookie可以存中文吗?
- tomcat8之前的版本cookie不能直接存储重温
- 需要将中文数据转码–一般采用URL编码(
%E3
)
- 需要将中文数据转码–一般采用URL编码(
- 在tomcat 8 之后,cookie支持中文数据。特殊字符(
空格
)还是不支持,建议使用URL编码存储,URL解码解析
2.3.4cookie共享问题?
-
1.在同一个
tomcat
服务器下,部署了多个web项目,这些项目之间的cookie可以共享吗?- 默认情况下不能共享
setPath(String path)
: 设置cookie的获取范围。默认情况下,设为为当前的虚拟目录,那么就只能在当前的项目内进行共享,如果要在不同的项目间共享,则设置为:setPath("/")
-
2不同的服务器之间的web项目的cookie可以共享吗?
- 可以的
setDomain(String path)
:如果设置一级域名相同,那么多个服务器之间cookie可以共享setDomain(".baidu.com")
,那么tieba.baidu.com
和news.baidu.com
中cookie可以共享
2.4cookie的特点和作用
- cookie的数据存储在客户端浏览器
- 浏览器对于单个cookie的大小有限制(4kb)以及对同一个域名下的cookie的数量也有限制(20)
- cookie一般用于存储少量不太敏感的数据
- 多用于在不登录的情况下,完成服务端对客户端的身份识别
- 例如在登录百度的情况下可以对百度的搜索进行一些设置
- 而且在重启浏览器后访问百度,这个设置依然有效
三、cookie案例:记住上一次的访问时间
1.需求
- 1.访问一个Servlet,如果是第一次访问,则提示:您好,欢迎首次访问
- 2.如果不是第一次访问,则提示:欢迎回来,您上一次的访问时间是:显示时间字符串
2.分析
- 可以利用cookie来完成
- 在服务器中有一个Servlet来判断是否有一个名为
lastTime
的cookie
- 1.没有:第一次访问
- 首先响应数据:您好,欢迎首次访问
- 其次回写cookie:
lastTime = 当前时间
- 2.有:
- 首先响应数据:您上一次的访问时间是:显示时间字符串
- 其次回写cookie:
lastTime = 当前时间
- 1.没有:第一次访问
3.代码实现
整体逻辑简单,两个注意实现
- cookie的name相同的话,会自动覆盖掉
- cookie默认不支持特殊字符,例如空格,会发生HTTP-500报错,使用URL编码解码进行结局
3.1代码
package com.zqx.cookie;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 20190816
* 利用cookie实现访问时间的显示与存储
*/
@WebServlet("/cookieTest")
public class CookieTest extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//0.设置响应消息体的数据格式以及编码
response.setContentType("text/html;charset=utf-8");
boolean flag = false; //默认没有访问过
//1.获取所有cookie
Cookie[] cookies = request.getCookies();
//2.遍历
for (Cookie cookie : cookies) {
//3.获取每一个cookie的name
String name = cookie.getName();
//4.判断name是否为指定的cookie名字 lastTime
if ("lastTime".equals(name)){
//有 说明不是第一次访问
flag = true;
//先获取当前的值
String value = cookie.getValue();
//设置cookie的value 会自动覆盖
//获取当前时间的字符串 设置cookie 然后发生
Date date = new Date();
//时间格式化输出
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String date_value = sdf.format(date);
//为了解决 时间字符串的空格 无法被cookie直接识别的问题 进行URL编码和解码
System.out.println("编码前:" + date_value);
//URL编码
date_value = URLEncoder.encode(date_value, "utf-8");
System.out.println("编码后:" + date_value);
//设置cookie值
cookie.setValue(date_value);
//设置存活时间 一个月
cookie.setMaxAge(60 * 60 * 24 * 30);
// 发生cookie
response.addCookie(cookie);
//响应 检测到有cookie 应取出当前的值(上一次访问时间)
//同样这里也是经过URL编码的 所以先解码
System.out.println("解码前:" + value);
value = URLDecoder.decode(value, "utf-8");
System.out.println("解码后:" + value);
//回写到界面
response.getWriter().write("<h1>欢迎回来,您上次访问时间为:"+value+"</h1>");
//只要访问到想要的cookie后面则不再循环
break;
}
}
if (cookies == null || cookies.length == 0 || !flag ){
//没有名为 lastTime的cookie 则第一次访问
Date date = new Date();
//时间格式化输出
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String date_value = sdf.format(date);
//为了解决 时间字符串的空格 无法被cookie直接识别的问题 进行URL编码和解码
System.out.println("编码前:" + date_value);
//URL编码
date_value = URLEncoder.encode(date_value, "utf-8");
System.out.println("编码后:" + date_value);
Cookie cookie = new Cookie("lastTime", date_value);
cookie.setMaxAge(60 * 60 * 24 * 30);
// 发生cookie
response.addCookie(cookie);
response.getWriter().write("<h1>您好,欢迎您首次访问</h1>");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
3.2效果
四、Session
保存在服务端的一次会话的共享数据
4.1快速入门
- 1.获取
HttpSession
对象:HttpSession session = request.getSession();
- 2.使用
HttpSession
对象:Object getAttribute(String name) ;
void setAttribute(String name, Object value);
void removeAttribute(String name)
@WebServlet("/sessionDemo1")
public class SessionDemo1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取Session对象
HttpSession session = request.getSession();
//2.设置数据
session.setAttribute("msg", "hello session");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
@WebServlet("/sessionDemo2")
public class SessionDemo2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取Session对象
HttpSession session = request.getSession();
//2.获取数据
Object msg = session.getAttribute("msg");
System.out.println(msg);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
4.2原理
Session的实现是依赖Cookie的
-
浏览器在第一次访问
sessionDemo1
的时候,服务器内存里没有Session
对象,则会创建一个Session
对象,并为其分配一个唯一的ID
-
浏览器第二次访问
sessionDemo2
的时候,获取到Session
对象,关键问题在于?如何确保第二次获取到的session
和上一次是通一个Session对象呢?- 第一次服务器响应的时候会在响应头里写入
Session
的ID
信息:set-cookie=JSESSIONID=2323819319
- 第二次浏览器发送请求的时候,会在请求头里包含该
set-cookie=JSESSIONID=2323819319
的信息 - 服务器就是这样确保两次访问的session是同一个对象的
- 可以看到,这个过程是通过把
session
的ID
保存在cookie
中完成的
- 第一次服务器响应的时候会在响应头里写入
-
下面通过抓包可以看到这两个过程:
4.3 细节
4.3.1当浏览器关闭,服务器不关闭,两次获取的Session是否为同一个?
- 默认情况下不是同一个,因为浏览器关闭了,意味着当前会话已经结束了嘛
-
获取一个Session,打印其地址,然后两次访问该Servlet
-
如果需要相同,则可以创建
cookie
,然后设置键JSESSIONID,设置最大存活时间,让cookie持久化保存。
-
//1.获取Session对象
HttpSession session = request.getSession();
//2. 使用Cookie持久化session 让关闭浏览器后 获取到的Session为同一个
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(60 * 60);
response.addCookie(cookie);
//2.打印session的内存地址
System.out.println(session);
}
4.3.2客户端不关闭,服务器关闭后,两次获取的Session是同一个吗?
-
不是同一个,因为服务器关闭后,web项目的内存空间肯定也关闭了,Session信息肯定丢失了。
-
但是这个Session的数据又很重要,需要想办法确保服务器关闭后也能保存下来。
-
例如,用户在淘宝刚刚加入了很多商品在购物车,这个购物车的信息就是保存在session中的,如果服务器关闭了,且session数据没有被保存,那么购物车的信息也就被丢失了。
-
所以,为了解决上述问题,tomcat服务器会自动的完成以下工作:
- session的钝化:在服务器正常关闭之前,将Session对象系列化到硬盘上
- session的活化:在服务器启动后,将session文件反序列化为内存中的Session即可
- 但是要注意
这种方法仅仅在手动部署项目到tomcat服务器才生效,使用IDEA联合配置是不生效的
- 至于为什么,下面通过实验来说明
-
1.首先找到当前项目部署在tomcat目录下的out文件
-
2.将其打包成
.war
的文件
-
3.将打包后的文件放在本地的tomcat的web-apps目录下
-
4.启动本地的tomcat项目(
bin/startup.bat
),发现刚才打包的项目被自动部署
-
访问其中的一个Servlet
-
正常关闭tomcat(
/bin/shutdown.bat
),观察work目录下的项目目录
-可以发现生成了一个Session文件,接下来,再次手动启动tomcat,观察现象
-
发现.ser文件消失,这是因为被自动加载到内存中去了
-
这个例子很清楚了说明了tomcat自动保存和加载session的过程
-
那么为什么IDEA不行呢?
-
先说结论,在IDEA中正常关闭服务器后,仍然会自动保存
-
但是重启服务器后,IDEA会自动删除work目录,再新建,自然session.ser也被删除了
4.3.3session何时被销毁
- 1.服务器关闭
- 2.session对象调用invalidate()
- 3.选择性配置修改
<session-config>
<session-timeout>30</session-timeout>
</session-config>
4.4session特点
- session用于存储一次会话的多次请求的数据,存在服务器端
- 常用在重定向的时候共享数据,ServletContex也可以用于重定向的数据共享,但是它的范围太大了
- session可以存储任意类型,任意大小的数据
- session与Cookie的区别:
1. session存储数据在服务器端,Cookie在客户端
2. session没有数据大小限制,Cookie有
3. session数据安全,Cookie相对于不安全