一、session简介
1.1 抛砖引玉
- 不同的用户登录网站后,不管该用户浏览该网站的哪个页面,都可显示登录人的名字,还可以随时去查看自己的购物车中的商品, 是如何实现的?
- 也就是说,一个用户在浏览网站不同页面时,服务器是如何知道是张三在浏览这个页面,还是李四在浏览这个页面?
1.2 解决之道—session 技术
- Session 是服务器端技术,服务器在运行时为每一个用户的浏览器创建一个其独享的session 对象/集合
- 由于 session 为各个用户浏览器独享,所以用户在访问服务器的不同页面时,可以从各自的 session 中读取/添加数据, 从而完成相应任务
二、session基本原理
2.1 示意图
-
当用户打开浏览器,访问某个网站, 操作 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)
三、session常用的方法
-
创建和获取 Session,API 一样
HttpSession hs=request.getSession();
-
向 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 值
四、session底层机制
4.1 session 底层实现机制图解(重要)
4.2 代码实现
- 创建session
public class CreateSession 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");
request.getSession().setAttribute("key1", "key1 的值");
response.getWriter().write("<h1>向 Session 对象 保存属性 key1</h1>");
}
}
测试 Session 创建的机制, 抓包分析
- 读取session
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生命周期
- 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 用一个线程来轮询会话状态,如果某个会话的空闲时间超过设定的最大值,则将该会话销毁
- session 是否过期,是由服务器来维护和管理
- 如我们调用了 invaliate() 会直接将该 session 删除/销毁
- 如果希望删除 session 对象的某个属性, 使用 removeAttribute(“xx”)
六、session经典案例-防止非法进入管理页面
6.1 需求说明
说明:
- 只要密码为 666666, 我们认为就是登录成功
- 用户名不限制
- 如果验证成功,则进入管理页面 ManageServelt.java ,否则进入 error.html
- 如果用户直接访问 ManageServet.java , 重定向到到 login.html
6.2 代码实现
两个html界面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录界面</title>
</head>
<body>
<h1>用户登录界面</h1>
<form action="/cookie/loginCheck" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="pwd"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录失败</title>
</head>
<body>
<h1>登录失败</h1>
<a href="/cookie/login.html">点击返回登录界面</a>
</body>
</html>
创建LoginCheckServlet:
package com.mzq.sessionhomework; /**
* @author MengZhiQiang
* @date 2022/4/11 21:57
* @version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class LoginCheckServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("LoginCheckServlet 被调用");
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
System.out.println("pwd = "+pwd);
if ("666666".equals(pwd)){
HttpSession session = request.getSession();
session.setAttribute("username",username);
request.getRequestDispatcher("/manage").forward(request,response);
}else {
response.sendRedirect(getServletContext().getContextPath()+"/err.html");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
创建ManageServlet:
package com.mzq.sessionhomework; /**
* @author MengZhiQiang
* @date 2022/4/11 22:05
* @version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class ManageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ManageServlet 被调用");
response.setContentType("text/html;charset=utf-8");
HttpSession session = request.getSession();
String username = (String)session.getAttribute("username");
if (username==null){
response.sendRedirect(getServletContext().getContextPath()+"/login.html");
}
PrintWriter writer = response.getWriter();
writer.println("<h1>用户管理界面</h1>\n" +
"<div>欢迎你 管理员:"+username+"</div>");
writer.flush();
writer.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
web.xml映射:
<servlet>
<servlet-name>LoginCheckServlet</servlet-name>
<servlet-class>com.mzq.sessionhomework.LoginCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginCheckServlet</servlet-name>
<url-pattern>/loginCheck</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ManageServlet</servlet-name>
<servlet-class>com.mzq.sessionhomework.ManageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ManageServlet</servlet-name>
<url-pattern>/manage</url-pattern>
</servlet-mapping>