日常生活中:从拨通电话到挂断电话之间的一连串你问我答的过程就是一个会话。
B/S架构中:从浏览器第一次给服务器发送请求时,建立会话;直到有一方断开,会话结束。
一次会话:包含多次请求响应。
1587172413825
**问题:**Http是一个无状态协议,同一个会话的连续两个请求相互独立,彼此并不了解
作用:用于 存储 浏览器与服务器在请求和响应过程中产生的 数据
在一次会话中(多次请求响应), 共享数据
客户端会话技术:cookie
服务器端会话技术:session
1587172824573
Cookie作用:在一次会话的多次请求之间共享数据,将数据保存到客户端(浏览器)
#Cookie的特点
1. cookie保存在客户端(浏览器), 往往是由服务器产生发送给浏览器
2. cookie只能保存字符串, 格式是 entry(name : value)
3. cookie的大小有限制: 4k
4. 一般, 同一域名下的cookie限制数量50个
下面我们首先可以以游客的身份访问京东页面,添加商品到购物车上,但是我们并没有登录京东的账号。当我们关闭京东的页面,再次访问的时候,却发现购物车还有我们之前加入购物车的商品。这是为什么呢?
这就是因为 Cookie 的作用了:京东的页面将游客加入购物车的商品信息保存到浏览器下,当使用同一个浏览器在一次会话中再次访问页面,那么商品信息就会自动随着cookie信息请求到 京东服务端,然后由京东服务将你之前选择的商品加入到购物车之中。
1591235563712
1591235580561
下面是谷歌浏览器请求访问京东服务器的示意流程图:
1591235608007
执行伪代码如下:
1. 设置数据到cookie中
// 1.创建cookie对象,设置数据
Cookie cookie = new Cookie(String name,String value);
// 2.通过response,响应(返回)cookie
response.addCookie(cookie);
2. 从cookie中获取数据
// 1.通过request对象,接收cookie数组
Cookie[] cookies = request.getCookies();
// 2.遍历数组
获取name值: String name = cookie.getName();
获取value值: String value = cookie.getValue();
首先我们需要创建页面 index.html, 模拟添加商品到购物车 和 第二次查看购物车的 操作。
image-20210216223410324
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<a href="Cookie01Servlet?product=xiaomi">第一次_添加小米到购物车</a> <br>
<a href="Cookie02Servlet">第二次_查看购物车</a> <br>
</body>
</html>
编写好 index.html 页面后,我们启动 tomcat 访问一下页面,如下:
image-20210216223444430
好了,下面我们就要实现 Cookie01Servlet 和 Cookie02Servlet 两个 Servlet 程序,来实现 Cookie 的业务操作。
创建 Cookie01Servlet 用来处理 添加商品到购物车:目的在于将添加的商品信息发送给服务器,由服务器返回给浏览器,将商品信息 设置 Cookie 信息 保存在浏览器。
image-20210217095805904
package com.lijw;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author Aron.li
* @date 2021/2/17 9:49
*/
@javax.servlet.annotation.WebServlet("/Cookie01Servlet")
public class Cookie01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 接收商品参数的信息
String product = request.getParameter("product");
//2. 创建Cookie对象
Cookie cookie = new Cookie("product", product);
//3. 返回浏览器添加Cookie
response.addCookie(cookie);
//4. 页面重定向回 index.html
response.sendRedirect("index.html");
}
}
创建 Cookie02Servlet 用来处理 第二次查看购物车:目的在于浏览器请求服务器的时候,会将同一个域名路径下的 Cookie 发送到服务器,然后由服务器将商品信息返回给浏览器显示。
image-20210217101143665
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 9:59
*/
@WebServlet("/Cookie02Servlet")
public class Cookie02Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置返回的内容为 html
response.setContentType("text/html;charset=utf-8");
// 1.获取请求中携带过来的所有 Cookie
Cookie[] cookies = request.getCookies();
// 2.遍历Cookies信息,显示到浏览器上
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
response.getWriter().write("name: " + name + ", value: " + value + "<br/>");
}
// 3. 返回 index.html 页面
response.getWriter().write("<a href='index.html'>返回index</a>");
}
}
启动 tomcat 服务,然后点击第一次_添加小米到购物车:
image-20210217101441237
image-20210217101509747
可以看到请求到了 Cookie01Servlet ,并且重定向回来了,下面看看添加好的 Cookie 信息,如下:
image-20210217101630401
image-20210217101704689
image-20210217101722464
可以看到成功显示出了所有 cookie 信息。
从上面的两个Servlet中,我们理解了如何添加 Cookie 以及 如何查询 Cookie 信息,下面我们来看看在浏览器如何查看 Cookie
image-20210217101905786
image-20210217101932923
google浏览器中查看Cookie的方式二
image-20210217101957329
基于HTTP协议:
1. 服务器发送Cookie给浏览器是通过 : 响应(响应头 set-cookie)
Set-Cookie: phone=xiaomi
Set-Cookie: computer=lenovo
2. 浏览器发送Cookie给服务器是通过: 请求(请求头 cookie)
Cookie: phone=xiaomi; computer=lenovo
1587174808482
* 答案: 可以的
// 1. 创建多个cookie对象
Cookie cookie1 = new Cookie("name","lucy");
Cookie cookie2 = new Cookie("age","18");
// 2. 通过response响应多个
response.addCookie(cookie1);
response.addCookie(cookie2);
下面我们写一个简单的示例来看看:
image-20210217102853105
image-20210217105855160
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 10:46
*/
@WebServlet("/Cookie03Servlet")
public class Cookie03Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 创建多个Cookie,返回浏览器
Cookie cookie1 = new Cookie("cookie1", "cookie1value");
Cookie cookie2 = new Cookie("cookie2", "cookie2value");
response.addCookie(cookie1);
response.addCookie(cookie2);
//2. 重定向回 index.html
response.sendRedirect("index.html");
}
}
image-20210217105957270
* tomcat8之前的版本,不支持中文
* tomcat8以后的版本,支持中文...
但是按照 Rfc6265Cookie规范,在cookie值中不能使用分号(;)、逗号(,)、等号(=)以及空格
我们可以存储,但是要使用url编码,来避开这个规范限制
* 解决
java.net.URLEncoder.encode(字符串","utf-8") 把字符串使用utf-8进行编码
java.net.URLDecoder.decode(字符串","utf-8") 把字符串使用utf-8进行解码
下面我们再写一个服务端设置 中文 和 非法字符 的 Cookie。
image-20210217110247192
image-20210217110549982
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 11:03
*/
@WebServlet("/Cookie04Servlet")
public class Cookie04Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 创建存储中文和非法字符的cookie内容(按照 Rfc6265Cookie规范,在cookie值中不能使用分号(;)、逗号(,)、等号(=)以及空格)
Cookie cookie = new Cookie("cookie04", "中文内容 空格 逗号内容");
//2. 返回浏览器,以及重定向
response.addCookie(cookie);
response.sendRedirect("index.html");
}
}
image-20210217110618332
image-20210217110639728
在上面的测试中,我们已经发现 Cookie 无法直接存储 中文 和 非法字符,为了解决这个问题。我们需要将内容进行 URL 编码。
image-20210217110926586
package com.lijw;
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.URLEncoder;
/**
* @author Aron.li
* @date 2021/2/17 11:03
*/
@WebServlet("/Cookie04Servlet")
public class Cookie04Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 创建存储中文和非法字符的cookie内容(按照 Rfc6265Cookie规范,在cookie值中不能使用分号(;)、逗号(,)、等号(=)以及空格)
String str = "中文内容 空格 逗号内容";
String encode = URLEncoder.encode(str, "utf-8"); //使用URL编码解决Cookie无法存储非法字符的问题
Cookie cookie = new Cookie("cookie04", encode);
//2. 返回浏览器,以及重定向
response.addCookie(cookie);
response.sendRedirect("index.html");
}
}
image-20210217111032266
下面我们再写一个读取 URL 编码的 Cookie 内容。
image-20210217112205049
image-20210217112247638
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 11:15
*/
@WebServlet("/Cookie05Servlet")
public class Cookie05Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8"); // 设置返回的内容为HTML
//1. 读取全部Cookie的信息
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
// 如果是cookie04,需要进行URL解码
if ("cookie04".equalsIgnoreCase(name)) {
// 对内容进行URL解码
String decode = URLDecoder.decode(value, "utf-8");
response.getWriter().write(name + "--" + decode + "<br/>");
} else {
// 打印其他Cookie内容
response.getWriter().write(name + "--" + value + "<br/>");
}
}
}
}
image-20210217112335728
image-20210217112351152
image-20210217112542516
1. cookie信息中的域名作用是标记这个cookie的归属
在我们的浏览器中,既保存了域名为localhost的cookie,又保存域名为baidu的cookie
那么访问的网站如果是 http://localhost:8080, 浏览器的请求只会携带域名为localhost的cookie
2. 默认情况下,cookie的域名是发送此cookie的服务器域名是一致的
url格式 -> 协议://域名:端口/资源位置
0. 在我们的项目中,cookie的路径默认为项目的虚拟路径
url-> http://localhost:8080/项目虚拟路径/Servlet的虚拟路径
比如: 项目虚拟路径 = /
1. 第一个作用: cookie信息中的path和name共同决定了cookie的唯一性
a. PathServlet被浏览器每访问一次, cookie就会发送一次
b. 如果服务器再次发送一个同 path+name的cookie,会覆盖浏览器的那个cookie
(新覆盖旧)
c. 服务器再次发送一个同 path, 异name的cookie , 不会覆盖
d. 服务器再次发送一个异 path, 同name的cookie, 不会覆盖
2. 第二个作用: cookie的path还决定了cookie允许被访问的范围
特点:Cookie在其设置的有效路径和其子路径下能够被访问到;
举例: 有一个cookie,路径为 /aaa
只有访问 http://localhost:8080/aaa 以及其子路径,才会携带这个cookie
问题:
1. 访问http://localhost:8080/PathServlet -> PathServlet
会携带这个cookie吗 -> 不会
2. 访问http://localhost:8080/aaa -> AaaServlet
会携带这个cookie吗 -> 会
3. 访问http://localhost:8080/aaa/MyServlet -> MyServlet
会携带这个cookie吗 -> 会
#API : cookie.setPath(虚拟路径);
路径要以 / 开头
下面我们来写一个简单的示例,编写 Cookie06Servlet 保存两个不同path 但是同 name 的cookie
首先修改 index.html 如下:
image-20210217152045596
编写 Cookie06Servlet 如下:
image-20210217152801055
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 15:21
*/
@WebServlet("/Cookie06Servlet")
public class Cookie06Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 创建两个Cookie,同name,但是path不一样
Cookie cookie1 = new Cookie("cookie06", "value01");
cookie1.setPath("/");
response.addCookie(cookie1);
Cookie cookie2 = new Cookie("cookie06", "value02");
cookie2.setPath("/a");
response.addCookie(cookie2);
}
}
测试请求,查看是否存在两个不同的 Cookie,如下:
image-20210217152837509
image-20210217152912147
image-20210217152925825
可以看到具有两个 cookie06,路径不一致。
第二个作用: cookie的path还决定了cookie允许被访问的范围
image-20210217153122515
# 浏览器中cookie的信息
1. 创建时间: 浏览器接收到此cookie的时间
2. 到期时间: cookie销毁的时间
# cookie的存活时间有两种
1. 会话级别(默认,浏览器关闭,cookie销毁 )
浏览器中的cookie显示(浏览会话结束时: 浏览器关闭)
原因: 浏览器将cookie保存内存中(临时的)
cookie在一个会话中(浏览器从打开到关闭: 访问服务器)共享数据
2. 持久级别(需要手动设置)
cookie.setMaxAge(int second); -- 单位是秒
正数:指定存活时间,持久化浏览器的磁盘中,到期后自动销毁
零:立即销毁
原因: 浏览器将cookie保存在硬盘上(持久的)
cookie在可以在多个会话中(浏览器从打开到关闭多次: 访问服务器)共享数据
编写一个设置cookie的时长示例。
image-20210217153340447
image-20210217153648640
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 15:34
*/
@WebServlet("/Cookie07Servlet")
public class Cookie07Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置存活时间为30分钟的cookie
Cookie cookie07 = new Cookie("cookie07", "30min");
cookie07.setMaxAge(60*30); // 30分钟
response.addCookie(cookie07);
// 重定向回 index.html
response.sendRedirect("index.html");
}
}
image-20210217153859689
可以看到 cookie07 的存活时间。
# 目标:删除Cookie
1. 用户在浏览器中手动删除cookie(清除浏览记录): 用户未必知道或者配合
2. 从服务端远程操控删除cookie(服务端)
# 远程删除实现步骤:
0. 核心思想: 服务端发送一个同 path+name的cookie,cookie的存活时间为0
原理: 新cookie先覆盖浏览器保存的cookie,但是因为时间0,新cookie马上死掉
1. 创建与要删除的cookie同name的cookie,将其值设置成什么都无所谓;
2. 设置这个cookie的路径(与原cookie的路径一致);
3. 将这个cookie的最大存活时间设置成0;
4. 将这个新的cookie响应给浏览器,置换原来的cookie,新cookie也马上销毁;
删除 cookie 的 Servlet 示例:
@WebServlet("/Cookie08Servlet")
public class Cookie08Servlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建一个短命cookie,跟要删除cookie同path+name
Cookie cookie = new Cookie("car", "");
cookie.setPath("/"); // http://localhost:8080/...
cookie.setMaxAge(0); // 设置存活时间只有0秒
response.addCookie(cookie);
}
}
1. cookie存储数据在客户端(浏览器)
2. cookie的存储数据(name和value)只能是字符串
3. cookie单个大小不能超过4KB
4. 同一个域名下cookie数量一般不能超过50个
5. 同一域名下, cookie的path和name决定了它的唯一性
6. cookie存储的数据不太安全
信息保存在用户的电脑上,都相对不安全
需求
做一个商品页面,当我们访问后,在页面上点击查看商品浏览记录后,可以查看到以前浏览过的商品信息。
image-20210217155259312
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="GoodsServlet?product=huawei">华为手机</a><br>
<a href="GoodsServlet?product=xiaomi">小米手机</a> <br>
<a href="GoodsServlet?product=chuizi">锤子手机</a> <br>
</body>
</html>
image-20210217155344702
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="goods.html">继续浏览</a> <br>
<a href="HistoryServlet">查看浏览记录</a> <br>
</body>
</html>
image-20210217160819321
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 15:54
*/
@WebServlet("/GoodsServlet")
public class GoodsServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 获取请求参数
String product = request.getParameter("product");
//2. 获取请求中的cookie
Cookie[] cookies = request.getCookies();
//2.1 判断是否存在 history 的 cookie
Boolean exist_history = false; // 用于标识history的cookie是否存在
if (cookies != null && cookies.length > 0){
for (Cookie cookie : cookies) {
String name = cookie.getName();
if("history".equalsIgnoreCase(name)){
// 设置标识存在 history cookie
exist_history = true;
// history的cookie存在
String value = cookie.getValue();
value = value + "_" + product;
cookie.setValue(value);
response.addCookie(cookie);
}
}
//判断history如果不存在,则创建新的Cookie
if (!exist_history) {
Cookie cookie = new Cookie("history", product);
cookie.setMaxAge(60*30);//30分钟
response.addCookie(cookie);
}
}
//3. 重定向到 goods2.html
response.sendRedirect("goods2.html");
}
}
image-20210217155643123
package com.lijw;
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;
/**
* @author Aron.li
* @date 2021/2/17 15:55
*/
@WebServlet("/HistoryServlet")
public class HistoryServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取请求中的cookie
Cookie[] cookies = request.getCookies();
if(cookies != null && cookies.length > 0){
for (Cookie cookie : cookies) {
String name = cookie.getName();
if("history".equalsIgnoreCase(name)){
String value = cookie.getValue();
String[] array = value.split("_");
response.getWriter().print("<h1>浏览器记录:</h1>");
for (String element : array) {
response.getWriter().print(element + "<br>");
}
}
}
}else{
response.getWriter().print("无浏览记录");
}
response.getWriter().print("<br>");
response.getWriter().print("<a href='goods.html'>继续浏览</a>");
}
}
image-20210217155801203
image-20210217160740185
image-20210217160752549