一、状态管理介绍
1.1 什么是状态管理
WEB应用中的会话是指一个客户端浏览器与WEB服务器之间连续发生的一系列请求和响应过程。
WEB应用的会话状态是指WEB服务器与浏览器在会话过程中产生的状态信息,借助会话状态,WEB服务器能够把属于同一会话中的一系列的请求和响应过程关联起来并达成数据共享
1.2 为什么需要状态管理
HTTP协议是无状态的,不能保存每次提交的信息,即当服务器返回与请求相对应的应答之后,这次事务的所有信息就丢掉了。 如果用户发来一个新的请求,服务器无法知道它是否与上次的请求有联系。 对于那些需要多次提交数据才能完成的Web操作,比如登录来说,就成问题了。通俗一点的例子就是,我们登录了在Github页面登录了,关闭页面之后,再次打开页面是不是发现你的Github还是保持你的账号登录的状态呢?这就是状态管理的作用!
1.3 状态管理的两种常见模式
客户端状态管理技术:将状态保存在客户端。代表性的是Cookie技术。
服务器状态管理技术:将状态保存在服务器端。代表性的是Session技术(服务器传递SessionID时需要使用Cookie的方式)。
二、状态管理之Cookie应用
2.1 什么是Cookie
Cookie是在浏览器访问WEB服务器的某个资源时,由WEB服务器在HTTP响应消息头中附带传送给浏览器的一小段数据,WEB服务器传送给各个客户端浏览器的数据是可以各不相同的。
一旦WEB浏览器保存了某个Cookie,那么它在以后每次访问该WEB服务器时,都应在HTTP请求头中将这个Cookie回传给WEB服务器。
WEB服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将Cookie信息发送给浏览器,浏览器则通过在HTTP请求消息中增加Cookie请求头字段将Cookie回传给WEB服务器。
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。但是要知道现在的技术可以传输数据的方式有很多,而且浏览器也可以对Cookie存放的最大数量做扩容机制,所以改限制不是一成不变的!
2.2 创建Cookie对象
/**
* 创建一个Cookie对象
* 默认生命周期为:浏览器关闭
*/
Cookie cookie = new Cookie("username", "ziph");
/**
* 更新Cookie的生命周期:负数->浏览器内存里 0->失效 正数->过期时间
* </p>
* 过期时间以秒为单位,如下表示:
* 浏览器请求后Cookie存活60秒,60秒过后就会过期
*/
cookie.setMaxAge(60);
/**
* 设置Cookie的共享范围: 默认同一项目下 /->当前服务器下
*/
cookie.setPath("/");
/**
* 将Cookie对象响应给浏览器
*/
response.addCookie(cookie);
2.2.1 关于Cookie生命周期的设置
设置Cookie生命周期时间核心代码:cookie.setMaxAge(); (设置生命周期为60秒)
- 设置时间值说明:
- 正数:有效期(单位:秒)
- 0:失效
- 负数:内存存储
/**
* 更新Cookie的生命周期:负数->浏览器内存里 0->失效 正数->过期时间
* </p>
* 过期时间以秒为单位,如下表示:
* 浏览器请求后Cookie存活60秒,60秒过后就会过期
*/
cookie.setMaxAge(60);
2.2.2 关于Cookie共享范围的设置
cookie 一般都是由于用户访问页面而被创建的,可是并不是只有在创建 cookie 的页面才可以访问这个cookie。在默认情况下,出于安全方面的考虑,只有与创建 cookie 的页面处于同一个目录或在创建cookie页面的子目录下的网页才可以访问。那么此时如果希望其父级或者整个网页都能够使用cookie,就需要进行路径的设置。
关于Cookie的共享范围,默认是同一项目下。而设置"/"可以在同意服务器下共享
/**
* 设置Cookie的共享范围: 默认同一项目下 /->当前服务器下
*/
cookie.setPath("/");
2.2.3 遍历查询Cookie
/**
* 1.获取所有Cookie
* 2.非空判断
* 3.遍历Cookies数组
* 4.检查遍历Cookie并获取name值是否为username
* 5.检查遍历Cookie并获取value值是否为ziph
* 6.两步检查都没有问题,则打印提示信息
*/
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length != 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("username")) {
if (cookie.getValue().equals("ziph")) {
System.out.println("您已登录!不用重新登录!");
}
}
}
}
2.3 Cookie的编码与解码问题
中文和英文字符不同,中文属于Unicode字符,在内存中占用4个字符,而英文属于ASCII字符,内存中只占2个字节。Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会出现乱码。编码可以使用java.net.URLEncoder类的encode(String str,String encoding)方法,解码使用java.net.URLDecoder类的decode(String str,String encoding)方法
/**
* Cookie的编码
* URLEncoder.encode("username", "utf-8")
*/
Cookie cookie = new Cookie(URLEncoder.encode("username", "utf-8"), URLEncoder.encode("ziph", "utf-8"));
/**
* Cookie的解码
* URLDecoder.decode(cookie.getName(), "utf-8")
*/
if (cookies.length != 0) {
for (Cookie cookie : cookies) {
if (URLDecoder.decode(cookie.getName(), "utf-8").equals("username")) {
if (URLDecoder.decode(cookie.getValue(), "utf-8").equals("ziph")) {
System.out.println("您已登录!不用重新登录!");
}
}
}
}
2.4 发送Cookie的条件
浏览器在发送请求之前,首先会根据请求url中的域名在cookie列表中找所有与当前域名一样的cookie,然后再根据指定的路径进行匹配,如果当前请求在域匹配的基础上还与路径匹配那么就会将所有匹配的cookie发送给服务器。
2.5 设置Cookie路径
通过Cookie的setPath方法设置路径
2.5 Cookie的优缺点
2.5.1 Cookie的优点
可配置到期规则: Cookie 可以在浏览器会话结束时到期,或者可以在客户端计算机上无限期存在,这取决于客户端的到期规则,不需要任何服务器资源,Cookie 存储在客户端并在发送后由服务器读取。
简单性: Cookie 是一种基于文本的轻量结构,包含简单的键值对。
数据持久性: 虽然客户端计算机上 Cookie 的持续时间取决于客户端上的 Cookie 过期处理和用户干预,Cookie 通常是客户端上持续时间最长的数据保留形式
2.5.2 Cookie的缺点
大小受到限制: 大多数浏览器对 Cookie 的大小有 4096 字节的限制,尽管在当今新的浏览器和客户端设备版本中,支持 8192 字节的 Cookie 大小已愈发常见。
用户配置为禁用: 有些用户禁用了浏览器或客户端设备接收 Cookie 的能力,因此限制了这一功能。
潜在的安全风险: Cookie 可能会被篡改。用户可能会操纵其计算机上的 Cookie,这意味着会对安全性造成潜在风险或者导致依赖于Cookie 的应用程序失败。
2.6 Cookie综合案例之站点获取上一次访问时间
关于访问站点,每一次访问都可以使用Cookie来记录时间,功能实现为本次访问获取上一次访问的系统时间。(注意:第一次访问时是没有上一次访问时间的,我们记录并打印第一次访问时间即可!)
代码展示在下面,注释中记载着详细步骤:
package com.mylifes1110.java.demo.cookievisit;
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.text.SimpleDateFormat;
import java.util.Date;
/**
* 获取上一次访问的时间案例
*/
@WebServlet(name = "CookieVisitServlet", value = "/cv")
public class CookieVisitServlet 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对象的数组
* 2.非空判断
* 3.遍历Cookie对象数组
* 4.寻找lastTime相同名字的Cookie对象
* 5.保存到临时cookie_avl中
*/
Cookie[] cookies = request.getCookies();
Cookie cookie_avl = null;
if (cookies != null && cookies.length != 0) {
for (Cookie cookie : cookies) {
if ("lastTime".equals(cookie.getName())) {
cookie_avl = cookie;
}
}
}
/**
* 1.创建时间格式化对象并指定格式
* 2.判断临时cookie_avl对象是否为空
* 3.cookie_avl对象为空,证明该站点是被第一次访问
* 4.获取当前时间对象
* 5.打印第一次访问时间(第一次访问肯定没有上一次访问时间啊)
* 6.获取当前时间毫秒值(因为Cookie是需要传入字符串,我们这里用空字符串做拼接,转换为字符串传入参数)存入cookie_avl对象
* 7.操作cookie_avl对象并发出响应
*/
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
if (cookie_avl == null) {
Date currentDate = new Date();
System.out.println("第一次访问时的时间为:" + format.format(currentDate));
cookie_avl = new Cookie("lastTime", currentDate.getTime() + "");
} else {
/**
* 1.获取上一次cookie_avl对象中的时间字符串,并将字符串转换为毫秒值
* 2.把毫秒值转换为该时间格式化的时间对象
* 3.打印上一次访问的时间对象
* 4.获取当前时间对象的毫秒值,并传入cookie_avl对象中保存
* 5.操作cookie_avl对象并发出响应
*/
long currentTimeMills = Long.parseLong(cookie_avl.getValue());
Date lastDate = new Date(currentTimeMills);
String lastDateStr = format.format(lastDate);
System.out.println("上一次访问时间为:" + lastDateStr);
Date currentDate = new Date();
cookie_avl.setValue(currentDate.getTime() + "");
}
response.addCookie(cookie_avl);
}
}
2.7 Cookie综合案例之显示浏览记录
页面显示商品,加入浏览记录放到Cookie中,通过显示浏览记录响应浏览器
HTML页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图书商城</title>
</head>
<body>
<h1>商品列表</h1>
<a href="/reqweb/history?id=0">《Java编程思想》</a><br>
<a href="/reqweb/history?id=1">《Java核心卷》</a><br>
<a href="/reqweb/history?id=2">《算法》</a><br>
<a href="/reqweb/history?id=3">《Ziph的博客》</a><br>
</body>
</html>
商品记录的Servlet
package com.mylifes1110.java.books.servlet;
import com.mylifes1110.java.books.utils.CookieUtils;
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;
/**
* 商品浏览记录
* 注意:我们将浏览记录存储在cookie_val对象中,以便后续展示页面取出加工后响应展示给浏览器
*/
@WebServlet(name = "HistoryBooksServlet", value = "/history")
public class HistoryBooksServlet 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 cookie_val = CookieUtils.getCookie(request.getCookies(), "history");
/**
* 1.获得一个Cookie数组对象