会话概念 :
会话 :
- 一次会话包含多次请求和相应
- 一次会话 : 浏览器第一次给服务器资源发送数据 , 会话建立 , 直到有一方断开为止
功能 :
- 在一次会话的多次请求间 共享数据
方式 :
- 客户端会话技术 : Cookie
- 将数据存到客户端的技术
- 服务器端会话技术 : Session
- 将数据存到服务器端的技术
Cookie (小饼干)
概念:
- 客户端会话技术 : Cookie
- 将数据存到客户端的技术
- 在第一次请求后 , 服务器端会向客户端发送一些数据 , 客户端将这些数据绑定在Cookie对象中 , 在下一次请求的时候 , 会将这些数据再发送给服务器 , 如果下一次请求换了资源路径 , 那么就需要在程序中编写代码 , 来讲这些数据共享
使用:
1.创建Cookie对象 , 绑定数据
-
new Cookie(String name , String value)
2.发送Cookie对象
-
response.addCookie(Cookie cookie)
3.获取Cookie , 拿到数据
-
Cookie[] request.getCookies();
Cookie原理:
基于响应头 set-cookie 和 请求头 cookie 实现
第一次请求的时候 , 没有Cookie数据 ,
到2的时候 , 服务器响应会发送一个Cookie的内容到浏览器 ,
3的时候 , 浏览器在一次会话中发送第二次请求 , 这个时候会夹带 2 中获取的Cookie一起 , 发送到服务器
Cookie的细节处理:
(1)一次可以发送多个Cookie
- 可以创建多个Cookie对象 , 使用response调用多次addCookie()方法发送即可
(2)设置Cookie在浏览器中保存时间
-
默认情况下 , 当浏览器关闭后 ,Cookie数据被销毁 (存储在浏览器的内存中)
-
设置Cookie的生命周期 , 持久化Cookie 使用方法:
-
Cookie.setMaxAge(int seconds) //传递一个秒数 //正数 : 将Cookie的数据写到硬盘的文件中 , 持久化存储 , Cookie的存活时间 //负数 : 默认值 //零 : 删除Cookie信息(物理文件) , 既不存在在内存中 , 也不存在硬盘中
-
(3)Cookie存储中文(URL编码解码)
-
在Tomcat8之前 , Cookie中不能直接存储中文数据
- 需要将中文数据转码 , — 一般采用URL编码
-
在Tomcat8之后 , Cookie支持中文
-
但是特殊字符还是不支持 , 建议使用URL编码存储 , URL解码来解析
-
//编码前的数据: 2022年02月02日16:55:05 //编码 URLEncoder 前边传要编码的数据 , 后边传编码的格式 String URLEncoder.encode(str_date, "utf-8"); //编码后的数据:2022%E5%B9%B402%E6%9C%8802%E6%97%A516%3A55%3A05 //解码 URLDecoder 前边传要解码的数据 , 后边传解码的格式 String URLDecoder.decode(value, "UTF-8"); //解码后的数据: 2022年02月02日16:55:05
(4)Cookie获取范围有多大?
①同一个Tomcat服务器下的项目
-
假设在一个Tomcat服务器中 , 部署了多个web项目 , 那么在这些项目中cookie能不能共享
- 默认情况下是不能共享的
-
也可以设置Cookie的获取范围 , 默认情况下设置的是当前项目的虚拟目录
-
setPath(String path) //设置Cookie的获取范围 cookie.setPath("/当前项目名");//默认值
-
如果想在服务器的其他项目中获取这个Cookie , 那么就需要设置更大的范围
-
cookie.setPath("/");//只写一个 / 表示当前项目的根路径 , 根路径下的所有项目都能共享
②不同Tomcat服务器下的项目
-
setDomain(String path) //:设置一级域名相同 , 那么多个服务器之间Cookie可以共享 //例如: //setDomain(".baidu.com"); 那么tieba.baidu.com 和 news.baidu.com 中的Cookie可以共享
Cookie的特点和作用:
特点 :
(1)Cookie存储数据是在客户端浏览器 , 很容易丢失和篡改
(2)浏览器对于单个Cookie的大小有限制(一般为4kb) , 以及 对同一个域名下的总cookie数量也有限制(一般是在20个左右) ,
作用 :
(1)cookie一般用来存储少量的不太敏感的数据
(2)在不登录的情况下 , 来完成服务器对客户端的识别 ,
比如 :设置网页属性的时候 , 在下次访问的的时候 , 网页会使用上次保存的cookie属性, 来展示画面
Cookie案例_记住上次访问时间
需求 :
1.访问一个servlet , 如果是第一次访问 , 则提示 : 你好 , 欢迎您首次访问
2.如果不是第一次访问 , 则提示 : 欢迎回来 , 您上次访问的时间为 :显示时间字符串
分析 :
在服务器的servlet中判断 , 是否有一个名为 lastTime的cookie
- 有 : 不是第一次访问 ,
- 响应数据 : 欢迎回来 : 您上次访问的时间是 : 2022年2月2日16:25:59
- 写回cookie : lastTime = 2022年2月2日16:26:40
- 没有 : 是第一次访问
- 响应数据 : 您好 , 欢迎您首次访问
- 写回cookie : lastTime = 2022年2月2日16:27:34
package cn.sichen.cookie;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
- 有 : 不是第一次访问 ,
- 响应数据 : 欢迎回来 : 您上次访问的时间是 : 2022年2月2日16:25:59
- 写回cookie : lastTime = 2022年2月2日16:26:40
- 没有 : 是第一次访问
- 响应数据 : 您好 , 欢迎您首次访问
- 写回cookie : lastTime = 2022年2月2日16:27:34
*/
@WebServlet("/CookieTest")
public class CookieTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取所有的Cookie , 判断是否有lastTime
Cookie[] cookies = request.getCookies();
boolean flag = false ;
//2.遍历Cookie数组 ,
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
//3.获取每一个cookie的名称
String name = cookie.getName();
//4.判断名字是否是 : lastTime
if ("lastTime".equals(name)){
//有该cookie , 不是第一次访问
flag = true ;
//响应数据
//获取cookie的数据
String value = cookie.getValue();
System.out.println("解码前 :"+value);
//URL解码 :
value = URLDecoder.decode(value, "UTF-8");
System.out.println("解码后"+value);
response.getWriter().write("欢迎回来 , 您上次访问的时间是: "+value);
//重新设置cookie的value
//获取当前时间的字符串 , 重新设置Cookie的value值
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = simpleDateFormat.format(date);
//对数据进行编码
System.out.println("编码前的数据: " + str_date);
//编码前的数据: 2022年02月02日16:55:05
//URl编码 :
str_date = URLEncoder.encode(str_date, "utf-8");
System.out.println("编码后的数据:" + str_date);
//编码后的数据:2022%E5%B9%B402%E6%9C%8802%E6%97%A516%3A55%3A05
cookie.setValue(str_date);
//重新设置cookie持久化存储
cookie.setMaxAge(60 * 60 * 24 * 30); //一个月
//写回cookie
response.addCookie(cookie);
break;
}
}
}
if (cookies == null || cookies.length == 0 || flag == false ){
//是第一次访问
//设置cookie的value
//获取当前时间的字符串 , 重新设置Cookie的value值
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = simpleDateFormat.format(date);
//对数据进行编码
str_date = URLEncoder.encode(str_date, "utf-8");
//新建一个cookie
Cookie cookie = new Cookie("lastTime" , str_date);
//设置cookie持久化存储
cookie.setMaxAge(60 * 60 * 24 * 30); //一个月
//写回cookie
response.addCookie(cookie);
response.getWriter().write("您好! 欢迎您首次访问");
}
}
}
Session (主菜)
概念 :
- 服务器端会话技术 : 在一次会话的多次请求间共享数据 , 将数据保存在服务器的对象中 , HttpSession
原理:
Session的实现是依赖于Cookie的
浏览器在一次会话的第一次发送请求之后 , 服务器 会创建一个Session , 这个Session有一个ID作为标识 , 服务器在这次请求响应的时候 , 会把这个ID 放在响应头中 以Cookie的形式传递到浏览器 , 浏览器在本次会话的下次请求时 , 会将这个ID包含在Cookie中发送到服务器 , 服务器通过标识 查找对应的Session
![1643806580277](https://gitee.com/aisichen/sichen/raw/master/img/1643806580277.png)
使用:
获取HttpSession对象:
HttpSession session = request.getSession();
//在HttpSession中有三个方法 :
Object getAttribute(String name);
void setAttribute(String name , Object value);
void removeAttribute(String name);
Session的细节实现:
(1)当客户端关闭后 , 服务器不关闭 , 两次获取的Session不是同一个Session
-
默认情况下不是 同一个
-
1中的session:org.apache.catalina.session.StandardSessionFacade@524defe9 //关闭浏览器后 , 再次访问 : 2中的session:org.apache.catalina.session.StandardSessionFacade@dbe882b
-
期望客户端关闭之后 , Session也能相同
-
这就要依赖于Cookie来完成
-
//获取Cookie , 键必须为JSESSIONID这个参数 , value是session的值 Cookie cookie = new Cookie("JSESSIONID", session.getId()); cookie.setMaxAge(60*60); //设置Cookie的生命周期 response.addCookie(cookie);
(2)客户端不关闭 , 服务器端关闭 : 两次获取的Session
-
不是同一个 : 服务器在关闭之后 , 资源就会释放 , 内存就被销毁了 ,
-
注意 : 虽然不是同一个 , 但是要确保数据不丢失 要使用:
- session的钝化 (序列化)
- 在服务器正常关闭之前 , 将session对象序列化到硬盘上
- session的活化 (反序列化)
- 在服务器启动后 , 将session文件转化为内存中的session对象即可
- session的钝化 (序列化)
-
这些功能 Tomcat会自动帮我们实现 , 在Tomcat服务器关闭的时候 , 会在Tomcat服务器根目录下的work中对应的项目中 , 生成一个SESSIONS.ser文件 , 这个文件就是被钝化的Session , 再次启动服务器的时候 , Tomcat服务器会自动读取这个文件内容进内存 .
-
关闭前的Session(简写) : @2275c9d5 再次访问的Session(简写) : @7083adc8 //注意 : 两次虽然地址值不同 , 但是可以打印出保存的数据 , 说明底层是将旧的Session与新的Session做了操作 , 让他们代表的是同一个Session
-
idea不支持这一动作 , 因为idea在关闭服务器的时候 , 会将这个文件保存在c盘下的一个文件(这里并不一定写的对 , 理解这个意思即可)的work目录里 , 但是再次打开的时候 , idea会先将这个work文件删除 , 然后就读取不到这个SESSIONS.ser文件了
(3)Session什么时间被销毁 ?
①服务器关闭的时候
②session对象去调用一个 invalidate()方法 会自己销毁自己
③session 默认的销毁时间是 30分钟 (在Tomcat中可以配置 )
-
Tomcat中的conf中有一个web.xml 是所有项目的父配置文件
-
-
这个就是默认的失效时间 单位是 分钟
当然你也可以在每一个项目中的web.xml文件中配置这一段代码 , 来覆盖父类的设置 , 这样就不影响其他的项目
Session的特点 :
(1) Session用于存储一次会话间的多次请求的数据 , 存在服务器端 ,
(2) Session可以存储任意类型的数据 和任意的大小
Session与Cookie的区别:
(1)Session存储数据在服务器端 , Cookie存储是在客户端
(2)Session 存储数据没有大小限制 , Cookie限制大小为4kb
(3)Session 数据是存在服务器端的 , 数据比较安全
购物车实现 :
购物车内容是存在一个map集合中 , 这个map集合是存在Session中的
案例_验证码判断
需求 :
- 访问一个带有验证码的登录页面 login.jsp
- 用户输入用户名 , 密码 以及验证码
- 如果用户输入用户名 或 密码有误 , 跳转到登录页面 , 提示: 用户名或密码错误
- 如果 验证码 输入有误, 跳转登录页面 , 提示 验证码错误
- 如果全部输入正确 , 则跳转到主页 , success.jsp , 显示 用户名 , 欢迎您
实现 :
- 在生成验证码的类中修改 :
//获取生成的验证码 , 存在一个session中 ,
//创建一个字符缓冲区
StringBuilder sb = new StringBuilder();
for (int x = 1; x <= 4; x++) {
int i = random.nextInt(str.length());
//获取字符
char c = str.charAt(i);//随机字符
//将产生的字符添加到缓冲区中
sb.append(c);
//2.3 写验证码
g.drawString(c+"" , width/5*x , height/2);
}
//转为字符串
String checkCode_session = sb.toString();
HttpSession session = request.getSession();
session.setAttribute( "checkCode_session",checkCode_session);
- loginServlet.java
package cn.sichen.servlet;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;
import java.util.Map;
@WebServlet("/loginServlet")
public class loginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置request编码
request.setCharacterEncoding("utf-8");
//2.获取参数Map
// Map<String, String[]> parameterMap = request.getParameterMap();
String username = request.getParameter("username");
String password = request.getParameter("password");
String checkCode = request.getParameter("checkCode");
//3.先获取生成的验证码
HttpSession session = request.getSession();
String checkCode_session = (String)session.getAttribute("checkCode_session");
//删除session中存储的验证码 , 确保验证码是一次性的
session.removeAttribute("checkCode_session");
//3.先判断验证码 是否正确
//将系统生成的放在前边 , 可以避开空指针异常的情况
if (checkCode_session != null && checkCode_session.equalsIgnoreCase(checkCode)){
//不区分大小写equalsIgnoreCase使用这个方法
//判断用户名和密码
if ("sichen".equals(username) && "acpl159357".equals(password)){
//登录成功
//存储用户信息
//这里应该储存用户的user对象 , 但是这里就暂时存为user
session.setAttribute("user" , username);
//重定向到success.jsp
response.sendRedirect(request.getContextPath()+"/success.jsp");
}else {
//登录失败
//存储提示信息 ,
request.setAttribute("error_login" , "用户名或密码错误!");
//转发到登录页面
request.getRequestDispatcher("/login.jsp").forward(request , response);
}
}else{
//验证码不一致:
//存储提示信息 ,
request.setAttribute("error_cc" , "验证码错误!");
//转发到登录页面
request.getRequestDispatcher("/login.jsp").forward(request , response);
}
}
}
- login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login</title>
<script>
window.onload = function (){
document.getElementById("img").onclick = function (){
var date = new Date().getTime();
this.src = "/Cookie/yanzhengServlet?time="+date;
}
}
</script>
<style>
div{
color: red;
}
</style>
</head>
<body>
<form action="/Cookie/loginServlet" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码 :</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>验证码:</td>
<td><input type="text" name="checkCode"></td>
</tr>
<tr>
<td colspan="2"><img id="img" src="/Cookie/yanzhengServlet"></td>
</tr>
<tr>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
<div><%=request.getAttribute("error_cc") == null? "":request.getAttribute("error_cc")%></div>
<div><%=request.getAttribute("error_login") == null?"":request.getAttribute("error_login")%></div>
</body>
</html>
:
<div><%=request.getAttribute("error_cc") == null? "":request.getAttribute("error_cc")%></div>
<div><%=request.getAttribute("error_login") == null?"":request.getAttribute("error_login")%></div>
---