JAVAWeb10-Web 开发会话技术-Session-02
1. session 有什么用
● 思考两个问题—抛砖引玉
- 不同的用户登录网站后,不管该用户浏览该网站的哪个页面,都可显示登录人的名字,还可以随时去查看自己的购物车中的商品, 是如何实现的?
- 也就是说,一个用户在浏览网站不同页面时,服务器是如何知道是张三在浏览这个页面,还是李四在浏览这个页面?
- 同学们想想,如果让你来实现这个功能,你会如何完成?
- 解决之道—session 技术, 简单说
-
Session 是服务器端技术,服务器在运行时为每一个用户的浏览器创建一个其独享的session 对象/集合
-
由于 session 为各个用户浏览器独享,所以用户在访问服务器的不同页面时,可以从各自的 session 中读取/添加数据, 从而完成相应任务
2. session 基本原理
2.1 Sesson 原理示意图
- 当用户打开浏览器,访问某个网站, 操作 session 时,服务器就会在内存(在服务端)为该浏览器分配一个 session 对象,该 session 对象被这个浏览器独占, 如图
- 这个 session 对象也可看做是一个容器/集合,session 对象默认存在时间为 30min(这是在tomcat/conf/web.xml),也可修改
2.2 Session 可以做什么
- 网上商城中的购物车
- 保存登录用户的信息
- 将数据放入到 Session 中,供用户在访问不同页面时,实现跨页面访问数据
- 防止用户非法登录到某个页面
- …
2.3 如何理解 Session
- session 存储结构示意图
- 你可以把 session 看作是一容器类似 HashMap,有两列(K-V),每一行就是 session 的一个属性。
- 每个属性包含有两个部分,一个是该属性的名字(String),另外一个是它的值(Object)
3. session 常用方法
3.1 文档
java_ee_api_中英文对照版.chm
3.2 Session 的基本使用
-
创建和获取 Session,API 一样
HttpSession hs=request.getSession();
第 1 次调用是创建 Session 会话, 之后调用是获取创建好的 Session 对象 -
向 session 添加属性
hs.setAttribute(String name,Object val);
-
从 session 得到某个属性
Object obj=hs.getAttribute(String name);
-
从 session 删除调某个属性:
hs.removeAttribute(String name);
-
isNew(); 判断是不是刚创建出来的 Session
-
每个 Session 都有 1 个唯一标识 Id 值。通过 getId() 得到 Session 的会话 id 值
4. session 底层实现机制
4.1 原理分析图(一图胜千言)
● session 底层实现机制图解(重要)
4.2 实例分析
- 需求:演示 Session 底层实现机制-创建和读取 Session
- (回顾之前手写Tomcat的项目)
添加容器:sessionMapping
public class HspTomcatV3 {
//1. 存放容器 servletMapping
// -ConcurrentHashMap
// -HashMap
// key - value
// ServletName 对应的实例
public static final ConcurrentHashMap<String, HspHttpServlet>
servletMapping = new ConcurrentHashMap<>();
//2容器 servletUrlMapping
// -ConcurrentHashMap
// -HashMap
// key - value
// url-pattern ServletName
public static final ConcurrentHashMap<String, String>
servletUrlMapping = new ConcurrentHashMap<>();
//你可以这里理解session, tomcat还维护一个容器
public static final ConcurrentHashMap<String, HttpSession>
sessionMapping = new ConcurrentHashMap<>();
。。。。。。。
}
- 创 建 CreateSession.java
public class CreateSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("CreateSession 被调用...");
//1. 获取session, 同时也可能创建session
HttpSession session = request.getSession();
//2. 给session获取id
System.out.println("CreateSession 当前sessionid= " + session.getId());
//3. 给session存放数据
session.setAttribute("email", "zs@qq.com");
//4. 给浏览器发送一个回复
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>创建/操作session成功...</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
测试 Session 创建的机制, 注意抓包分析:
(按照分析图3种情况测试分析请求、响应的信息)
补充说明:
当浏览器第一次访问Tomcat服务器时,Tomcat会为该会话创建一个唯一的标识符JsessionId。这是因为JsessionId是用于跟踪用户会话的关键参数,可以确保在同一个会话期间用户的多次请求都被映射到同一个会话上下文中处理。在后续的请求中,浏览器将JsessionId作为cookie或URL参数发送给服务器,以便服务器能够识别并恢复与该特定会话相关联的所有状态和信息。
补充:
启动Tomcat默认会访问一个jsp文件,此时会生成返回一个JsessionId,但是没有为该id生成对象
- 创 建 测试读取
ReadSession.java
public class ReadSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("ReadSession 被调用...");
// 演示读取session
//1. 获取session, 如果没有sesion, 也会创建
HttpSession session = request.getSession();
//输出sessionId
System.out.println("ReadSession sessionid= " + session.getId());
//2. 读取属性
Object email = session.getAttribute("email");
if (email != null) {
System.out.println("session属性 email= " + (String) email);
} else {
System.out.println("session中没有 email属性 ");
}
//给浏览器回复一下
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>读取session成功...</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
测试 Session 读取的机制, 注意抓包分析
- 有了代码支撑,我们在回头看 Session 的原理图,就有更深刻的理解
4.3 Session 实现原理动画
● 服务器是如何实现一个 session 为一个用户浏览器服务的
5. session 生命周期
5.1 Session 生命周期-说明
-
public void setMaxInactiveInterval(int interval) 设置 Session 的超时时间(以秒为单位),超过指定的时长,Session 就会被销毁。
-
值为正数的时候,设定 Session 的超时时长。
-
负数表示永不超时
-
public int getMaxInactiveInterval()获取 Session 的超时时间
-
public void invalidate() 让当前 Session 会话立即无效
-
如果没有调用 setMaxInactiveInterval() 来指定 Session 的生命时长,Tomcat 会以 Session默认时长为准,Session 默认的超时为 30 分钟, 可以在 tomcat 的 web.xml 设置
-
Session 的生命周期指的是 :客户端/浏览器两次请求最大间隔时长,而不是累积时长。即当客户端访问了自己的 session,session 的生命周期将从 0 开始重新计算。(解读: 指的是同一个会话两次请求之间的间隔时间)
-
底层: Tomcat 用一个线程来轮询会话状态,如果某个会话的空闲时间超过设定的最大值,则将该会话销毁
5.2 Session 生命周期-应用实例
● 需求:代码演示说明 Session 的生命周期
● 代码实现
- 创 建 CreateSession2.java
public class CreateSession2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("CreateSession2 被调用");
//创建session
HttpSession session = request.getSession();
System.out.println("CreateSession2 sid= " + session.getId());
//设置生命周期为 60s
session.setMaxInactiveInterval(60);
session.setAttribute("u", "jack");
//回复一下浏览器
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>创建session成功, 设置生命周期60s</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- 创 建 ReadSession2.java
public class ReadSession2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("ReadSession2 被调用...");
//1. 获取到session
HttpSession session = request.getSession();
System.out.println("ReadSession2 sid= " + session.getId());
//2. 读取session的属性
Object u = session.getAttribute("u");
if (u != null) {
System.out.println("读取到session属性 u= " + (String) u);
} else {
System.out.println("读取不到session属性 u 说明原来的session被销毁");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
解读:Session 的生命周期
- 指的是两次访问 session 的最大间隔时间
- 如果你在 session 没有过期的情况下,操作 session,则会重新开始计算生命周期
- session 是否过期,是由服务器来维护和管理
- 如我们调用了 invaliate() 会直接将该,session 删除/销毁
- 如果希望删除 session 对象的某个属性, 使用 removeAttribute(“xx”)
- 创 建 DeleteSession.java
public class DeleteSession extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("DeleteSession 被调用...");
//演示如何删除session
HttpSession session = request.getSession();
session.invalidate();
//再多说一句, 如果你要删除session的某个属性
//session.removeAttribute("xxx");
//回复一下浏览器
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>删除session成功</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
完成测试 , 使用前面编写的文件来演示说明 Session 的生命周期.
6. Session 经典案例-防止非法进入管理页面
6.1 作业布置
- 需求说明: 完成防止用户登录管理页面应用案例(如图)
说明:
- 只要密码为 666666, 我们认为就是登录成功
- 用户名不限制
2.1 如果验证成功,则进入管理页面 ManageServelt.java
2.2 如果验证失败,则进入 error.html
- 如果用户直接访问 ManageServet.java , 重定向到到 login.html
登录界面:userLogin.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="/cs/loginCheck"
method="post">
用户名:<input type="text" name="username"/><br/><br/>
密 码:<input type="password" name="password"><br><br/>
<input type="submit" value="登录"></form>
</body>
</html>
登录失败页面:error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录失败</title>
</head>
<body>
<h1>登录失败</h1>
<!--
回顾 web工程路径专题
1. a 标签是 浏览器解析
2. 第一 / 被解析成 http://localhost:8080/
3. 如果没有 / 会以当前浏览器地址栏 的 http://localhost:8080/工程路径../资源 去掉资源部分作为参考路径
4 其它的回顾请大家看 web工程路径专题~~ , 他会贯彻 整个java后端开发
-->
<a href="/cs/userlogin.html">点击重新登录</a>
</body>
</html>
LoginCheckServlet.java
public class LoginCheckServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("LoginCheckServlet 被调用..");
//功能-> 自己拆解 -> 逐步实现 [大量练习]
//1. 得到提交的用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
if("666666".equals(password)) {//认为合法
//把用户名保存到 session
HttpSession session = request.getSession();
session.setAttribute("loginuser", username);
//请求转发到ManageServlet
request.getRequestDispatcher("/manage").forward(request, response);
} else {
//请求转发进入到 error.html
request.getRequestDispatcher("/error.html").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
ManageServlet.java
public class ManageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断该用户是否登录过
HttpSession session = request.getSession();
Object loginuser = session.getAttribute("loginuser");
if(loginuser == null) {//说明该用户没有登录
//重新登录-> 请求重定向
//response.sendRedirect("/cs/userlogin.html");
response.sendRedirect(request.getContextPath() + "/userlogin.html");
return;
} else {
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>用户管理页面</h1>");
writer.println("欢迎你, 管理员:" + loginuser.toString());
writer.flush();
writer.close();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}