一、状态管理
1.1 为什么需要状态管理
HTTP协议是无状态的,不能保存每次提交的信息,即当服务器返回与请求相对应的应答之后,这次事务的所有信息就丢掉了。
如果用户发来一个新的请求,服务器无法知道它是否与上次的请求有联系。
对于那些需要多次提交数据才能完成的Web操作,比如登录来说,就成问题了。
1.2 什么是状态管理
WEB应用中的会话是指一个客户端浏览器与WEB服务器之间连续发生的一系列请求和响应过程。
WEB应用的会话状态是指WEB服务器与浏览器在会话过程中产生的状态信息,借助会话状态,WEB服务器能够把属于同一会话中的一系列的请求和响应过程关联起来。
1.3 状态管理的两种常见模式
- 客户端状态管理技术:将状态保存在客户端。代表性的是Cookie技术。
- 服务器状态管理技术:将状态保存在服务器端。代表性的是session技术(服务器传递sessionID时需要使用Cookie的方式)和application
1.4 两者的关系图
二、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
2.2 Cookie常用方法
基本步骤:
- 创建Cookie对象
- 设置有效期
- 设置有效路径
- 设置协议读取
- 把cookie放入response响应中,给浏览器
@WebServlet(name = "CookieServlet",value = "/cookieservlet")
public class CookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//cookie本质就是键值对,而且只能是字符串类型,这里中文需要转码,但是不同浏览器情况不同,根据情况进行处理
Cookie cookie = new Cookie("username", URLEncoder.encode("张三", "utf-8"));
//设置属性
//1.设置有效期,单位说秒,正数表示过多少秒过期,存储在硬盘和内存,负数表示不存储,只保存在内存中,浏览器退出删除,0表示立即删除
cookie.setMaxAge(60*60*24);
//2.设置有效路径,给浏览器用,单独斜杠表示localhost:8080
cookie.setPath("/");
//3.设置只有Http协议读取
cookie.setHttpOnly(true);
//4.把cookie放入response响应中,给浏览器
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
读取Cookie
cookie.getName()方法获取键值
cookie.getValue()方法获取值
@WebServlet(name = "ReadCookieServlet", value = "/readcookie")
public class ReadCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//读取浏览器回传过来的cookie
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie cookie : cookies) {
//如果设置了编码,则读取的时候需要解码
System.out.println(cookie.getName() + "....." + URLDecoder.decode(cookie.getValue(), "utf-8"));
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
更新Cookie
注意:如果键相同,即name,有效路径相同,就是同一个cookie,会覆盖浏览器的cookie
@WebServlet(name = "UpdateServlet", value = "/updatecookie")
public class UpdateServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//更新cookie
//如果名字相同name,有效路径相同,就是同一个cookie,会覆盖浏览器的cookie
Cookie cookie = new Cookie("username", URLEncoder.encode("李四", "utf-8"));
cookie.setMaxAge(60*60);
cookie.setPath("/");
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
三、状态管理-Session
3.1 什么是Session
Session用于跟踪客户的状态。Session指的是在一段时间内,单个客户与Web服务器的一连串相关的交互过程。
在一个Session中,客户可能会多次请求访问同一个网页,也有可能请求访问各种不同的服务器资源。
每个Session都存储有一个id号,第一次响应的时候反馈给浏览器。
3.2 Session工作原理
session被用于表示一个持续的连接状态,在网站访问中一般指代客户端浏览器的进程从开启到结束的过程。session其实就是网站分析的访问(visits)度量,表示一个访问的过程。
session的常见实现形式是cookie(session cookie),即未设置过期时间的cookie,这个cookie的默认生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。实现机制是当用户发起一个请求的时候,服务器会检查该请求中是否包含sessionid,如果未包含,则系统会创造一个名为JSESSIONID的输出 cookie返回给浏览器(只放入内存,并不存在硬盘中),并将其以HashTable的形式写到服务器的内存里面;当已经包含sessionid时,服务端会检查找到与该session相匹配的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的 session。这里需要注意的是session始终是有服务端创建的,并非浏览器自己生成的。
3.3 常用方法
Session可以作为一个容器进行存储数据使用,并在各个servlet中通过新建Session都可以进行获取,不像request,数据传输不依赖重定向或者转发。
@WebServlet(name = "SessionServlet", value = "/sessionservlet")
public class SessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取Session对象,相当于一个集合,可以存储东西
HttpSession session = request.getSession();
//2.获取session信息
String id = session.getId();
System.out.println("session的ID: " + id);
//3.获取创建时间
long time = session.getCreationTime();
System.out.println("创建时间:" + new Date(time));
//4.获取最后访问时间
long lastAccessedTime = session.getLastAccessedTime();
System.out.println("最后一次访问时间:" + new Date(lastAccessedTime));
//5. 存储数据
session.setAttribute("username", "张三");
//6. 读取session中的数据,一经存储,只要网页不关闭,其他servlet类也可以get获取
String username = (String) session.getAttribute("username");
System.out.println(username);
//7.设置有效期,默认是1800秒,还可以在web.xml里配置
session.setMaxInactiveInterval(20 * 60);
//8. 获取session的有效期,单位是秒,从最后一次访问起计时
System.out.println("session的有效期:" + session.getMaxInactiveInterval());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
//打印结果
session的ID: 3083F11A52BEA23152E6270377CDD01B
创建时间:Mon Sep 02 19:34:12 CST 2019
最后一次访问时间:Mon Sep 02 19:34:12 CST 2019
张三
session的有效期:1200
通过配置web.xml文件设置session有效期
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<session-config>
<!--单位是分钟-->
<session-timeout>10</session-timeout>
</session-config>
</web-app>
删除Session需要手动处理,因为即使关闭了网页,Session依然在服务器中,毕竟是由服务器创建的
失效的几种情况:
(1)超过了设置的超时时间
(2)主动调用了invalidate方法
(3)服务器主动或异常关闭,浏览器关闭并不会让Session失效
@WebServlet(name = "InvalidateSessionServlet",value = "/invalidatesessionservlet")
public class InvalidateSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//手动使session失效,因为即使关闭网页,服务器中还是存在
HttpSession session = request.getSession();
session.removeAttribute("username"); //首先要将所存储的属性一个个清理掉
session.invalidate();
System.out.println(session.getId() + "失效了");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
浏览器禁用Cookie的解决方案
一般用户都不会禁用Cookie,但是如果有人禁用了Cookie,则有部分网站无法登陆,而且Session也无法使用,服务器在默认情况下,会使用Cookie的方式将sessionID发送给浏览器,如果用户禁止Cookie,则sessionID不会被浏览器保存,此时,服务器可以使用如URL重写这样的方式来发送sessionID。
实现方法:
//如果是链接地址和表单提交,使用
response.encodeURL(String url)生成重写后的URL
//如果是重定向,使用
response.encodeRedirectURL(String url)生成重写的URL
案例:验证码登录
前端登录界面,参考《servlet的使用——response和request》案例二
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="loginservlet" method="post" enctype="application/x-www-form-urlencoded">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
验证码:<input type="text" name="validcode" />
<!--vs是验证码servlet,提供的图片-->
<img id="coding" src="/0902web1/vs" onclick="change()">
<a href="" onclick="return change()">看不清</a><br/>
<input type="submit" value="登录"/>
</form>
<script type="text/javascript">
function change() {
var coding = document.getElementById("coding");
coding.src = "/0902web1/vs?n=" + Math.random();
return false;
}
</script>
</body>
</html>
验证码servlet,用session存储验证码信息,方便在loginservlet中进行验证比对
@WebServlet(name = "ValidateCodeServlet" , value = "/vs")
public class ValidateCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ValidateCode validateCode = new ValidateCode(120, 30, 4, 20);
//将验证码存储
String code = validateCode.getCode();
HttpSession session = request.getSession(); //创建session,将验证码的字符串存入
//把验证码放入session
session.setAttribute("vCode", code);
validateCode.write(response.getOutputStream()); //图片需要二进制流输出
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
登录servlet,用来进行比对验证
@WebServlet(name = "LoginServlet",value = "/loginservlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String username = request.getParameter("username");
String password = request.getParameter("password");
String validcode = request.getParameter("validcode");//获取验证码
HttpSession session = request.getSession();
PrintWriter writer = response.getWriter();
//判断验证码
String vCode = (String) session.getAttribute("vCode");
if (!validcode.equalsIgnoreCase(vCode)) {
writer.println("验证码输入有误");
return;
}
if (username.equals("admin") && password.equals("123")) {
//登录成功,将信息放入session
session.setAttribute("username", username);
writer.println("<h1>登录成功</h1>");
} else {
writer.println("<h1>登录失败</h1>");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}