会话技术(Cookie和Session)

会话概念 :

会话 :

  • 一次会话包含多次请求和相应
    • 一次会话 : 浏览器第一次给服务器资源发送数据 , 会话建立 , 直到有一方断开为止

功能 :

  • 在一次会话的多次请求间 共享数据

方式 :

  • 客户端会话技术 : 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 实现

1643782354334

第一次请求的时候 , 没有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

使用:

获取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对象即可
  • 这些功能 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 是所有项目的父配置文件

  • 1643813497277

  • 这个就是默认的失效时间 单位是 分钟

当然你也可以在每一个项目中的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>



---

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值