【会话技术基础】

【会话技术基础】

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

  • 功能:在一次会话的范围内的多次请求间,共享数据
  • 方式:
    • 客户端会话技术:Cookie
    • 服务器端会话技术:Session

一、 客户端会话技术:Cookie

概念:客户端会话技术,将数据保存到客户端

1. Cookie 快速入门

  • 使用步骤:

    1. 创建 Cookie 对象,绑定数据:new Cookie(String name,String value)
    2. 发送 Cookie 对象:response.addCookie(Cookie cookie)
    3. 获取 Cookie 对象,拿到数据:Cookie[] request.getCookies()
  • 为方便创建 Servlet 实现类,在 IDEA 中修改代码模板,按下图方式操作
    模板

@WebServlet("/cookieTest01")
public class CookieTest01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 创建 Cookie 对象
        Cookie cookie = new Cookie("msg", "Hello");
        // 2. 发送 Cookie 对象
        response.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
@WebServlet( "/cookieTest02")
public class CookieTest02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 3. 获取 Cookie 对象
        Cookie[] cookies = request.getCookies();
        // 获取数据,遍历 Cookies
        if (cookies!=null){
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                String value = cookie.getValue();
                System.out.println(name+":"+value);
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

2. Cookie 实现原理

  • 基于响应头 set-cookie请求头 cookie 实现

过程解释(以上述代码为例):

  • 有客户端浏览器和服务器,在服务器中编写了两个资源 CookieTest01(发送 Cookie ) 和 CookieTest02(获取 Cookie );
  • 此时客户端发送请求 CookieTest01 的资源,服务器做出响应,发送 Cookie 给客户端(此时 Cookie 中保存的数据是 msg:Hello);
  • 响应的详细过程: Response 对象设置了一个响应头set-cookie:msg=Hello(键值对形式) ;
  • 客户端将响应头携带的数据(msg=Hello)保存到客户端浏览器上,等到下一次发送请求时,客户端会将这个数据带到服务器;
  • 第二次请求的详细过程: Request 对象设施一个请求头cookie:msg=Hello(键值对形式),在服务器中可以获取请求头中的数据,一般使用 JavaWeb 封装好的 API 进行操作

3. Cookie 的使用

  1. 一次可以发送多个 Cookie 对象
  2. Cookie 对象的存活时间
  3. Cookie 对象存储中文数据
  4. Cookie 对象的获取范围(数据共享)
(1) 一次发送多个 Cookie 对象

可以创建多个 Cookie 对象,使用 Response 对象调用多次 addCookie() 方法发送 Cookie 对象即可

@WebServlet( "/cookieTest03")
public class CookieTest03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie cookie01 = new Cookie("msg", "Hello");
        Cookie cookie02 = new Cookie("name", "John");
        response.addCookie(cookie01);
        response.addCookie(cookie02);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
(2)Cookie 对象的存活时间
  1. 默认情况下,当浏览器关闭后,Cookie 数据被销毁
  2. 持久化存储:
    • setMaxAgent(int seconds)
      1. 正数:将 Cookie 数据写到硬盘的文件中,进行持久化存储,传递的参数就是设置 Cookie 存活的时间
      2. 负数:默认值
      3. 零:删除 Cookie 信息
@WebServlet( "/cookieTest04")
public class CookieTest04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 创建 Cookie 对象
        Cookie cookie = new Cookie("msg", "setMaxAge()");
        // 设置 Cookie 的存活时间
        cookie.setMaxAge(30); // 将 Cookie 持久化到硬盘,30秒后就会自动删除 Cookie 文件
        // 发送 Cookie
        response.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
(3)Cookie 对象存储中文数据
  • 在 Tomcat 8 之前 Cookie 中不能直接存储中文数据
    • 需要将中文数据转码,但是一般采用 URL 编码(%E3)
  • 在 Tomcat 8 之后 Cookie 中支持存储中文数据,但是特殊字符还是不支持,建议使用 URL 编码存储,URL 解码解析
(4)Cookie 对象的获取范围(数据共享)
  • 在一个 Tomcat 服务器中,部署了多个 Web 项目,这些 Web 项目中 Cookie 在默认情况下不能共享数据
  • setPath(String path):设置 Cookie 的获取范围,默认情况下为设置的当前的虚拟目录。如果要进行数据共享。则可以将 path 设置为 "/"
@WebServlet("/cookieTest05")
public class CookieTest05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie cookie = new Cookie("msg", "你好!");
        // 设置 path ,让当前的服务器下的部署的所有项目共享 Cookie 信息
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

  • 不同的 Tomcat 服务器(不同的服务器)间的 Cookie 共享:setDomain(String path) :如果设置一级域名相同,那么多个服务器之间的 Cookie 可以共享数据(例如:setDomain(".baidu.com") 那么 tieba.baidu.comnews.baidu.com 中的 Cookie 就可以共享)

4. Cookie 的特点和作用

  • 特点:

    • Cookie 存储数据在客户端浏览器
    • 浏览器对于单个 Cookie 的大小有限制(4kb)以及对同一个域名下的总 Cookie 数量也有限制(20个)
  • 作用:

    • Cookie 一般用于存储少量的不太敏感的数据
    • 在不登陆的情况下,完成服务器对客户端的身份识别

5. 案例:记住上次访问时间

需求:

  • 访问一个 Servlet 实现类,如果是第一次访问,则提示:您好,欢迎首次访问
  • 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串

分析:

  • 可以采用 Cookie 来完成
  • 在服务器中创建 Servlet 实现类,该类中创建一个名为 lastTime 的 Cookie ,之后访问时判断浏览器中是否有这个 Cookie
    1. 存在:不是第一次访问
      1. 响应数据:欢迎回来,您上次访问的时间为:具体时间值
      2. 写回 Cookie :lastTime = 当前具体时间
    2. 不存在:是第一次访问
      1. 响应数据:您好,欢迎您首次访问
      2. 写回 Cookie :lastTime = 当前具体时间
@WebServlet( "/cookieTest06")
public class CookieTest06 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置相应的消息体的数据格式以及编码
        response.setContentType("text/html;charset=utf-8");
        // 获取所有 Cookie
        Cookie[] cookies = request.getCookies();
        boolean flag=false; // 没有 Cookie 为 lastTime
        // 遍历 Cookie 数组
        if (cookies!=null&&cookies.length>0){
            for (Cookie cookie : cookies) {
                // 获取 Cookie 的名称
                String name = cookie.getName();
                // 判断名称是否是:lastTime
                if ("lastTime".equals(name)){
                    // 有该 Cookie,不是第一次访问
                    flag=true; // 有 lastTime 的 Cookie
                    // 设置 Cookie 的 value
                    // 获取当前时间的字符串,重新设置 Cookie 的值,重新发送 Cookie
                    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);
                    // URL 编码
                    str_date = URLEncoder.encode(str_date, "utf-8");
                    System.out.println("编码后:"+str_date);
                    cookie.setValue(str_date);
                    // 设置 Cookie 的存活时间
                    cookie.setMaxAge(60*60*24); // 一天
                    response.addCookie(cookie);
                    // 响应数据
                    // 获取 Cookie 的 value、时间
                    String value = cookie.getValue();
                    System.out.println("解码前:"+value);
                    // URL 解码
                    value= URLDecoder.decode(value,"utf-8");
                    System.out.println("解码后:"+value);
                    response.getWriter().write("<h1>欢迎回来,您上次访问时间为:"+value+"</h1>");
                    break;
                }
            }
        }
        if (cookies==null||cookies.length==0||flag==false){
            // 没有,第一次访问
            // 设置 Cookie 的 value
            // 获取当前时间的字符串,重新设置 Cookie 的值,重新发送 Cookie
            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);
            // URL 编码
            str_date = URLEncoder.encode(str_date, "utf-8");
            System.out.println("编码后:"+str_date);
            Cookie cookie = new Cookie("lastTime",str_date);
            // 设置 Cookie 的存活时间
            cookie.setMaxAge(60*60*24); // 一天
            response.addCookie(cookie);
            response.getWriter().write("<h1>您好,欢迎您首次访问!</h1>");
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

二、 服务器端会话技术:Session

概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中,使用 HttpSession 对象。

1. Session 快速入门

  1. 获取 HttpSession 对象:
    HttpSession session = request.getSession();
  2. 使用 HttpSession 对象:
    • Object getAttribute(String name)
    • void setAttribute(String name,Object value)
    • void removeAttribute(String name)
@WebServlet("/sessionTest01")
public class SessionTest01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 使用 Session 共享数据
        // 获取 Session 对象
        HttpSession session = request.getSession();
        // 存储数据
        session.setAttribute("msg","Hello,Session!");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
@WebServlet("/sessionTest02")
public class SessionTest02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取 Session 对象
        HttpSession session = request.getSession();
        // 获取数据
        Object msg = session.getAttribute("msg");
        System.out.println(msg);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

2. Session 实现原理

服务器是如何确保在一次会话范围内,多次获取的 Session 对象使用一个的?
Session 的实现是依赖于 Cookie 的!(Cookie 是依赖于请求头和响应头实现的)

  • 在服务器中,第一次获取 Session 时,没有 Cookie ,此时会在内存中创建一个新的 Session 对象,并且该 Session 对象具有一个唯一的 ID
  • 当浏览器发送请求,服务器做出响应的时候,服务器会发送一个响应头:set-cookie:JSESSIONID=ID 值set-cookie 头)
  • 之后客户端浏览器就将 set-cookie 头的信息存储到浏览器中
  • 当浏览器访问该项目中的其他资源的时候就会携带着该 set-cookie 头的数据,通过一个叫做 cookie 的请求头携带
  • 之后请求头就会去内存中查找是否有和 cookie 信息中 ID 吻合的 Session 对象
  • 之后服务器再根据 request.getSession() 方法找到该 Session 对象

3. Session 的使用

  1. 当客户端关闭后,服务器不关闭,两次获取的 Session 对象默认情况下,两次获取的 Session 对象不是同一个
    • 如果需要相同,则可以创建 Cookie 对象,键名为 JSESSIONID ,设置最大存活时间,让 Cookie 持久化存储
@WebServlet("/sessionTest03")
public class SessionTest03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取 Session 对象
        HttpSession session = request.getSession();
        System.out.println(session);
        // 期望浏览器关闭后, Session 对象也能相同
        Cookie cookie = new Cookie("JSESSIONID", session.getId());
        cookie.setMaxAge(60*60); // 设置存活时间为一个小时
        response.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
  1. 当客户端不关闭,服务器关闭后,两次获取的 Session 对象不是同一个

    • 但是如果要确保数据不丢失:(Tomcat 服务器可以自动做这两个过程,但是 IDEA 不能实现这两个过程(IDEA 只会实现钝化,无法活化),需要手动在本地的 Tomcat 服务器操作
      • Session 的钝化:在服务器正常关闭之前,将 Session 对象序列化到硬盘上
      • Session 的活化:在服务器启动后,将 Session 文件转化为内存中的 Session 对象即可
      • Tomcat 本地操作实现这两个过程:
        文件
        将如图三个文件打成 war 包放在:\webapps 目录下正常手动启动 Tomcat 服务器,项目就会被部署,正常手动关闭 Tomcat 服务器,Tomcat 服务器就会自动将 Session 对象序列化到硬盘上,在目录 work\Catalina\localhost 中(SESSIONS.ser 文件,里面存储的就是 Session 对象),一旦服务器被再次启动,文件将会被自动读取,该文件被删除掉,Session 对象就会被还原到内存中,虽然地址值不同,但是数据和 ID 值都保持与原来一致
  2. Session 对象的失效时间(销毁)

    1. 服务器关闭
    2. Session 对象调用 invalidate() 方法
    3. Session 对象的默认失效时间为:30分钟(在 \conf\web.xml 文件中)
      可以之后设置覆盖该设置
<session-config>
	<session-timeout>30</session-timeout>
</session-config>

4. Session 的特点

  1. Session 用于存储一次会话的多次请求的数据,存储在服务器端
  2. Session 可以存储任意类型,任意大小的数据
  • Session 与 Cookie 的区别:
    • Session 存储数据在服务器端,Cookie 存储数据在客户端
    • Session 没有数据大小限制,Cookie 有数据大小限制
    • Session 数据安全,Cookie 相对于不安全

5. 案例:验证码

  • 需求:

    1. 访问带有验证码的登录界面 login.jsp
    2. 用户输入用户名,密码以及验证码
      • 如果用户名和密码有误,跳转到登录界面,提示:用户名或者密码错误!
      • 如果验证码输入有误,跳转到登录界面,提示:验证码错误!
      • 如果全部输入正确,则跳转到主页 success.jsp,显示:用户名,欢迎您!
  • 分析:

    1. 设置 Request 对象的编码
    2. 获取参数 Map 集合
    3. 获取验证码
    4. 将用户信息封装到 User 对象中
    5. 判断程序生成的验证码和用户输入的验证码是否一致,从 Session 对象中获取程序生成的验证码
      • 一致:再判断用户名和密码是否正确:(需要连接数据库,编写 UserDao 类判断)
        • 正确:登录成功
        • 使用 Session 对象存储数据
        • 使用重定向跳转到 success.jsp 页面
        • 不正确:给出提示信息,跳转到登录界面
      • 不一致:
        • 使用 Request 对象给用户提示信息:验证码错误
        • 之后使用转发跳转登录界面
    6. 在 Request 域对象中获取数据,在 login.jsp 页面中显示错误信息
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script>
        window.οnlοad=function () {
            document.getElementById("img").οnclick=function () {
                this.src="/cookie_test/checkCodeServlet?time="+new Date().getTime();
            }
        }
    </script>
    <style>
        div{
            color: red;
        }
    </style>
</head>
<body>
<form action="/cookie_test/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_test/checkCodeServlet"></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="登录"></td>
        </tr>
    </table>
</form>
<div><%= request.getAttribute("cc_error")==null?"":request.getAttribute("cc_error") %></div>
<div><%= request.getAttribute("login_error")==null?"":request.getAttribute("login_error") %></div>
</body>
</html>
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width=100;
        int height=50;
        // 创建一个对象,在内存中的验证码图片对象
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 美化图片
        // 填充背景色
        Graphics graphics = image.getGraphics(); // 画笔对象
        graphics.setColor(Color.CYAN); // 设置画笔颜色
        graphics.fillRect(0,0,width,height);
        // 画边框
        graphics.setColor(Color.BLUE);
        graphics.drawRect(0,0,width-1,height-1);
        // 写验证码
        String str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        // 生成随机角标
        Random random = new Random();
        StringBuilder stringBuilder=new StringBuilder();
        for (int i = 1; i <= 4; i++) {
            int index = random.nextInt(str.length());
            // 获取字符,随机字符
            char charAt = str.charAt(index);
            stringBuilder.append(charAt);
            graphics.drawString(charAt+"",width/5*i,height/2);
        }
        String checkCode_session = stringBuilder.toString();
        // 将验证码存入 Session 达到共享数据的目的
        request.getSession().setAttribute("checkCode_session",checkCode_session);
        // 画干扰线
        graphics.setColor(Color.pink);
        // 随机生成坐标点
        for (int i = 0; i < 10; i++) {
            int x1 = random.nextInt(width);
            int x2 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int y2 = random.nextInt(height);
            graphics.drawLine(x1,x2,y1,y2);
        }
        // 将图片输出到页面展示
        ImageIO.write(image,"jpg",response.getOutputStream());
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置 Request 对象的编码
        request.setCharacterEncoding("utf-8");
        // 获取参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String checkCode = request.getParameter("checkCode");
        // 先获取生成的验证码
        HttpSession session = request.getSession();
        String  checkCode_session = (String) session.getAttribute("checkCode_session");
        // 删除 Session 中存储的验证码
        session.removeAttribute("checkCode_session");
        // 判断验证码是否正确
        if (checkCode_session!=null&&checkCode_session.equalsIgnoreCase(checkCode)){
            // 忽略大小写判断,比较字符串
            // 如果验证码正确,判断用户名和密码是否一致
            if ("张三".equals(username)&&"123".equals(password)){
                // 此时需要调用 UserDao 查询数据库
                // 登录成功
                // 存储信息,用户信息
                session.setAttribute("user",username); // 应该是存储 User 对象
                // 重定向到 success.jsp
                response.sendRedirect(request.getContextPath()+"/success.jsp");
            }else {
                // 登录失败
                // 存储提示信息到 Request
                request.setAttribute("login_error","用户名或密码错误!");
                // 转发到登录界面
                request.getRequestDispatcher("/login.jsp").forward(request,response);
            }

        }else {
            // 验证码不一致
            // 存储提示信息到 Request
            request.setAttribute("cc_error","验证码错误!");
            // 转发到登录界面
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1><%= request.getSession().getAttribute("user") %>,欢迎您!</h1>
</body>
</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值