Appearance
会话技术
案例目标
- 案例一 使用 Cookie 技术,记录用户的上次访问时间
- 案例二 使用 Session 技术,实现购物车功能
概念介绍
什么是会话
由多次请求和响应组成的网络上的活动
会话技术【了解】
Cookie
tex在客户端保存用户数据的一种技术 数据存储在客户端本地,减少服务器端的存储的压力,安全性不好,客户端可以清除cookie
Session
tex在服务端保存用户数据的一种技术 将数据存储到服务器端,安全性相对好,增加服务器的压力
会话的作用
HTTP 协议是无状态的网络传输协议。会话技术就是为了解决多次请求和响应时,服务器识别或区分客户端状态的技术。
小结
- 把用户数据存储在服务器的技术:session
- 把用户数据存储在客户端的技术:cookie
Cookie
相关API概要
Cookie入门
- Cookie(String name, String value)
- String Cookie.getName()
- String Cookie.getValue()
- Cookie[] HttpServletRequest.getCookies()
- void HttpServletResponse.addCookie(Cookie cookie)
练习
- CookieSendServlet.java
java
package com.futureweaver.web;
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;
// 服务器发送给浏览器,让浏览器保存数据
@WebServlet("/cookie_send")
public class CookieSendServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 通过构造方法,创建Cookie
// 将cookie发送到浏览器
response.addCookie(new Cookie("name", "Carl"));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
- CookieReceiveServlet.java
java
package com.futureweaver.web;
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.io.PrintWriter;
// 浏览器向服务器发送给cookie,服务器要接收这个cookie
@WebServlet("/cookie_receive")
public class CookieReceiveServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
// 获取cookie
// 因为是浏览器发送给服务器的,所以这个api在request里边
Cookie[] cookies = request.getCookies();
if (cookies == null) return;
for (Cookie cookie : cookies) {
out.println(cookie.getName() + ": " + cookie.getValue());
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
设置cookie为中文的解决方案
static String URLUncoder.encode(String s, String enc)
tex将字符串(s)按编码格式(enc)编码
static String URLDecoder.decode(String s, String enc)
tex将字符串(s)按解码格式(enc)解码
练习
- CookieSendServlet.java
java
package com.futureweaver.web;
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;
@WebServlet("/cookie_send")
public class CookieSendServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.addCookie(new Cookie("name", URLEncoder.encode("张三", "utf-8")));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
- CookieReceiveServlet.java
java
package com.futureweaver.web;
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.io.PrintWriter;
import java.net.URLDecoder;
@WebServlet("/cookie_receive")
public class CookieReceiveServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
Cookie[] cookies = request.getCookies();
if (cookies == null) return;
for (Cookie cookie : cookies) {
out.println("<H1>" + cookie.getName() + ": " + URLDecoder.decode(cookie.getValue(), "utf-8") + "</H1>");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结
Cookie
response.addCookie(cookie)
服务器向浏览器发送
request.getCookies();
服务器接收浏览器的发送
注意: 如果浏览器一个都不发,那么获取到的是null
Cookie(String name, String value)
getName()
getValue()
中文乱码
URLEncoder.encode("中文", "utf-8")
URLDecoder.decode("%..%..%..", "utf-8")
请求体、响应体、请求头、响应头乱码解决方案对比
Cookie设置过期时间
API
Cookie.setMaxAge(int expiry)
tex设置Cookie过期的时间,单位是秒。 如果值为正数,则表示cookie将在数秒之后过期。 负值意味着cookie不是持久存储的,将在Web浏览器退出时删除。 零值会导致cookie被删除。
练习
- CookieClearServlet.java
java
package com.futureweaver.web;
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
@WebServlet("/cookie_clear")
public class CookieClearServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 先遍历所有的cookie
Cookie[] cookies = request.getCookies();
// 如果浏览器当中没有任何一个cookie的话,那么getCookies会返回一个null
// 需要对空指针做校验
if (cookies == null) return;
for (Cookie cookie : cookies) {
// 只是单纯地把浏览器发送过来的cookie,作为一个对象来接收
// 接收到了之后,在服务器内部把这个对象的最大年龄设置为了0
// 但是,没有发送给浏览器
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
Cookie设置访问路径
API
Cookie.setPath(String uri)
tex用来设置cookie的访问路径,以后浏览器必须访问这个路径或它的子路径,浏览器才将cookie的信息发送给服务器。
练习
java
package com.futureweaver.web;
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;
@WebServlet("/cookie_access")
public class CookieAccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置一个localhost:8080也能够发送的Cookie
Cookie deep = new Cookie("deepPath", "deepCookieValue");
deep.setPath("/");
response.addCookie(deep);
// 设置一个只在当前项目中才会被发送的Cookie
Cookie context = new Cookie("contextPath", "contextCookieValue");
context.setPath(request.getContextPath());
response.addCookie(context);
// 设置一个在当前项目中,指定的目录中(shallow),才会被发送的Cookie
Cookie shallow = new Cookie("shallowPath", "shallowCookieValue");
shallow.setPath(request.getContextPath() + "/shallow");
response.addCookie(shallow);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结
把用户数据存储在浏览器的技术:Cookie
把用户数据存储在服务器的技术: Session
new Cookie("name", "value")
response.addCookie(cookie)
Cookie[] request.getCookies()
cookie.getName()
cookie.getValue()
URLEncoder.encode("字符串", "编码集")
URLDecoder.deocde("字符串", "编码集")
注意:编码和解码采用一致的编码集
过期时间:setMaxAge(秒)
立即销毁:setMaxAge(0)
setPath("uri")
案例
- 使用 Cookie 技术,记录用户的上次访问时间
分析
实现
- CookieUtils
java
package com.futureweaver.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
// Cookie相关工具类,用于查找Cookie
public class CookieUtils {
// 根据cookie的name,查询cookie的value
public static String getCookieValue(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return null;
}
for (Cookie cookie : cookies) {
if (cookie.getName().equals(cookieName)) {
return cookie.getValue();
}
}
return null;
}
}
- AccessTimeServlet
java
package com.futureweaver.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
// 用户上次访问时间Demo(Cookie)
@WebServlet("/access_time")
public class AccessTimeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 因为在响应体里边有中文,所以先设置一下边编码
response.setContentType("text/html; charset=utf-8");
// "上次访问时间"的Cookie,定义名字为"visitTime"
String visitTime = CookieUtils.getCookieValue(request, "visitTime");
// 要往响应体里边写字符,所以先获取字符输出流
PrintWriter out = response.getWriter();
String now = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date());
out.println("<H1>现在的时间是: " + now + "</H1>");
if (visitTime != null) {
out.println("<H1>您上次访问的时间是" + URLDecoder.decode(visitTime, "utf-8") + "</H1>");
}
// 要发送给浏览器"服务器当前时间"
response.addCookie(new Cookie("visitTime", URLEncoder.encode(now, "utf-8")));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结
HttpSession
相关API概要
HttpSession入门
API
- HttpSession Request.getSession()
- String HttpSession.getId()
- 获取 Session 的 ID(唯一标识)(箱子的票号)
- long HttpSession.getCreationTime()
- 获取 Session 的创建时间
- new SimpleDateFormate("yyyy-MM-dd HH:mm:ss").format(getCreationTime())
- long HttpSession.getLastAccessedTime()
- 获取 Session 的上次访问时间
- boolean HttpSession.isNew()
- 判断 Session 是否是新创建
- ServletContext HttpSession.getServletContext()
练习
java
package com.futureweaver.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
@WebServlet("/session_demo")
public class SessionDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
// 获取sesion
HttpSession session = request.getSession();
out.println("Session Id: " + session.getId() + "</BR>");
out.println("Creation Time: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(session.getCreationTime()) + "</BR>");
out.println("Last Accessed Time: " + new Timestamp(session.getLastAccessedTime()) + "</BR>");
out.println("Is New: " + session.isNew() + "</BR>");
out.println("Servlet Context: " + session.getServletContext() + "</BR>");
// 设置最大非活动时间(每访问一次,即活动一次)
// session.setMaxInactiveInterval(10);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
Session设置过期时间【重点】
API
HttpSession.setMaxInactiveInterval()
tex单独设置Session的非活动(过期)时间(秒)
HttpSession.invalidate()
tex立即销毁Session
web.xml【了解】
xml<session-config> <!-- 分钟 --> <session-timeout>5</session-timeout> </session-config>
request.getSession()
session.setMaxInactiveInterval(秒)
session.invalidate();
练习
用户登录后,设置会话 10 秒钟过期
用户退出后,设置会话失效
LoginServlet.java
java
package com.futureweaver.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取session,这个session,和SessionServlet当中的session,是同一个
HttpSession session = request.getSession();
// 设置最大非活动时间
session.setMaxInactiveInterval(10);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
- SignOutServlet.java
java
package com.futureweaver.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/sign_out")
public class SignOutServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取session,这个session,和SessionServlet当中的session,是同一个
HttpSession session = request.getSession();
// 立即销毁session
session.invalidate();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
getSession的内部活动
- 第 1 次用户访问没有会话,由服务器调用 s()创建一个会话对象。分配了一个会话 ID 给当前的用户。
- 创建一个 Cookie,名字叫:JSESSIONID,把会话 ID 发送给浏览器。
- 浏览器默认是浏览器关闭 Cookie 就过期,只要不关闭 Cookie 就会将会话 ID 发送给服务器
- 服务器得到上次的会话 ID,与内存中的会话比较,找到它的会话空间继续访问。
- 不同的用户,会话 ID 是不同的。
问题:Session不见了,会话丢了,什么原因?
- cookie 丢了
- 超时时间过了
- 浏览器关掉了,cookie 也不会见
- session 被销毁了
- invalidate
- setMaxInactiveInterval
- 配置时间到了
会话域【重点】
- void HttpSession.setAttribute(String name, Object value)
- 往域当中设置一个属性
- Object HttpSession.getAttribute(String name)
- 从域当中拿一个属性
- void HttpSession.removeAttribute(String name)
- 从域当中删除一个属性
案例
- 使用 Session 技术,实现购物车功能
需求分析
index.html
tex商品选择页 选择商品后,提交至AddToCartServlet
AddToCartServlet.java
tex将当前用户挑选的商品,添加至购物车
BalanceServlet.java
tex展示当前用户挑选的商品
实现
- index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="add_to_cart" method="post">
华为手机<input type="checkbox" name="good" value="华为手机"/><br/>
小米手机<input type="checkbox" name="good" value="小米手机"/><br/>
ThinkPad<input type="checkbox" name="good" value="ThinkPad"/><br/>
中兴手机<input type="checkbox" name="good" value="中兴手机"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
- AddToCartServlet.java
java
package com.futureweaver.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// 把用户提交上来的商品,添加到购物车里面
// 用户勾选的商品,在参数里边,参数的名字是good
// 用户的购物车,使用session技术进行存储
// sesion作用域存储的数据是键值对的,键值对当中的键,叫"goods"
// session goods所对应的值是List类型
@WebServlet("/add_to_cart")
public class AddToCartServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置请求体和响应体的字符编码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
// 获取用户提交上来的商品
String[] paramGoods = request.getParameterValues("good");
// 获取session
HttpSession session = request.getSession();
// 先要获取这个用户之前的购物车里边放的List
List<String> goods = (List<String>) session.getAttribute("goods");
// 因为有可能用户第一次访问,那么session作用域当中,有可能没有任何的数据
// 没有任何数据的话,应该给用户创建一个新的List
if (goods == null) {
goods = new ArrayList<>();
}
// 因为用户可能直接请求到了这个Servlet,不从html过来,那么good这个参数就会获取一个空
// 需要先判断空指针校验
if (paramGoods != null) {
// 将一个数据转换成List
List<String> strings = Arrays.asList(paramGoods);
// // 因为第二次请求时,把这个strings直接覆盖掉了,所以要处理
// session.setAttribute("goods", strings);
goods.addAll(strings);
}
// 因为用户可能第一次请求,所以这个对象,要重新设置回去
session.setAttribute("goods", goods);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
- BalanceServlet.java
java
package com.futureweaver.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
// 将用户购物车里面的商品,读出来,然后放到响应里边
// 因为购物车是放到了session当中,所以获取购物车里边的商品,就从session当中,去读
// 定义好的:session当中的对象名字是goods
// 定义好的:session当中的对象的值是List<String>
@WebServlet("/balance")
public class BalanceServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
// 因为要调用session的getAttribute方法,所以先拿到一个session
HttpSession session = request.getSession();
List<String> goods = (List<String>) session.getAttribute("goods");
// 因为用户可能直接来访问结算,所以还需要做一遍空指针校验
if (goods != null) {
out.println("<h1>您好,以下是您在购物车当中的商品</h1>");
for (String good : goods) {
out.println("<h3>"+good+"</h3>");
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
Servlet三个作用域总结
- 作用域对象的获得方式
作用域 | 获得方式 |
---|---|
请求域 | request参数 |
会话域 | request.getSession() |
上下文域 | getServletContext() request.getServletContext() session.getServletContext() |
- 作用域的生命周期
作用域 | 接口名 | 作用范围 | 生命周期 |
---|---|---|---|
请求域 | HttpServletRequest | 1个用户的1个请求 | 服务器接收到浏览器的访问时创建 响应结束时销毁 |
会话域 | HttpSession | 1个用户的所有请求 | 用户第一访问时被创建 会话(session)过期 |
上下文域 | ServletContext | 所有用户的所有请求 | 项目加载开启时创建 项目卸载关闭时销毁 |
- 三个作用域共同的方法
功能 | 方法 |
---|---|
存放数据 | setAttribute(String name, Object value); |
获取数据 | Object getAttribute(String name) |
删除数据 | removeAttribute(String name) |
小结
- 相关 API
getCreationTime
获取Session的创建时间(1970-01-01至今的毫秒数)
getId
箱子的票号
getLastAccessedTime
获取上一次的访问时间(1970-01-01至今的毫秒数)
setMaxInactiveInterval
设置Session的最大非活动时间
每一次浏览器进行访问时,就相当一次活动
invalidate
立即销毁Session
isNew
Session是否是新创建的
getAttribute
获取数据
setAttriburte
设置(存放)数据
removeAttribute
删除数据
- getSession 的内部活动
- HttpSession 是一个域对象
- 三个作用域的获得、生命周期
Request 的创建、销毁
一个用户的一次请求
Session 的创建、销毁
一个用户的所有请求
ServletContext 的创建、销毁
所有用户的所有请求
学习目标总结
- 能够理解会话技术
- 能够应用 request 和 response 对象操作 cookie
- 能够应用 cookie 对象的方法
- 能够应用 cookie 完成显示用户上一次访问时间的案例
- 能够应用 session 对象的方法
- 能够理解域对象的含义