Skip to content

会话技术

案例目标

  • 案例一 使用 Cookie 技术,记录用户的上次访问时间
  • 案例二 使用 Session 技术,实现购物车功能

概念介绍

什么是会话

Session

由多次请求和响应组成的网络上的活动

会话技术【了解】

  • Cookie

    tex
    在客户端保存用户数据的一种技术
    
    数据存储在客户端本地,减少服务器端的存储的压力,安全性不好,客户端可以清除cookie
  • Session

    tex
    在服务端保存用户数据的一种技术
    
    将数据存储到服务器端,安全性相对好,增加服务器的压力

会话的作用

HTTP 协议是无状态的网络传输协议。会话技术就是为了解决多次请求和响应时,服务器识别或区分客户端状态的技术。

小结

  • 把用户数据存储在服务器的技术:session
  • 把用户数据存储在客户端的技术:cookie

相关API概要

Cookie_Class_Hireachy

Cookie入门

  • Cookie(String name, String value)
  • String Cookie.getName()
  • String Cookie.getValue()
  • Cookie[] HttpServletRequest.getCookies()
  • void HttpServletResponse.addCookie(Cookie cookie)

练习

  • CookieSendServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// 服务器发送给浏览器,让浏览器保存数据
@WebServlet("/cookie_send")
public class CookieSendServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 通过构造方法,创建Cookie
        // 将cookie发送到浏览器
        response.addCookie(new Cookie("name", "Carl"));
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • CookieReceiveServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// 浏览器向服务器发送给cookie,服务器要接收这个cookie
@WebServlet("/cookie_receive")
public class CookieReceiveServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();

        // 获取cookie
        // 因为是浏览器发送给服务器的,所以这个api在request里边
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return;
        for (Cookie cookie : cookies) {
            out.println(cookie.getName() + ": " + cookie.getValue());
        }
    }

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

设置cookie为中文的解决方案

  • static String URLUncoder.encode(String s, String enc)

    tex
    将字符串(s)按编码格式(enc)编码
  • static String URLDecoder.decode(String s, String enc)

    tex
    将字符串(s)按解码格式(enc)解码

练习

  • CookieSendServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

@WebServlet("/cookie_send")
public class CookieSendServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.addCookie(new Cookie("name", URLEncoder.encode("张三", "utf-8")));
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • CookieReceiveServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;

@WebServlet("/cookie_receive")
public class CookieReceiveServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return;
        for (Cookie cookie : cookies) {
            out.println("<H1>" + cookie.getName() + ": " + URLDecoder.decode(cookie.getValue(), "utf-8") + "</H1>");
        }
    }

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

小结

  • Cookie

    response.addCookie(cookie)

    服务器向浏览器发送

    request.getCookies();

    服务器接收浏览器的发送

    注意: 如果浏览器一个都不发,那么获取到的是null

    Cookie(String name, String value)

    getName()

    getValue()

  • 中文乱码

    URLEncoder.encode("中文", "utf-8")

    URLDecoder.decode("%..%..%..", "utf-8")

  • 请求体、响应体、请求头、响应头乱码解决方案对比

Encoding

Cookie设置过期时间

API

  • Cookie.setMaxAge(int expiry)

    tex
    设置Cookie过期的时间,单位是秒。
    如果值为正数,则表示cookie将在数秒之后过期。
    负值意味着cookie不是持久存储的,将在Web浏览器退出时删除。
    零值会导致cookie被删除。

练习

  • CookieClearServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// 删除所有的cookie
@WebServlet("/cookie_clear")
public class CookieClearServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 先遍历所有的cookie
        Cookie[] cookies = request.getCookies();

        // 如果浏览器当中没有任何一个cookie的话,那么getCookies会返回一个null
        // 需要对空指针做校验
        if (cookies == null) return;

        for (Cookie cookie : cookies) {
            // 只是单纯地把浏览器发送过来的cookie,作为一个对象来接收
            // 接收到了之后,在服务器内部把这个对象的最大年龄设置为了0
            // 但是,没有发送给浏览器
            cookie.setMaxAge(0);
            response.addCookie(cookie);
        }
    }

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

Cookie设置访问路径

API

  • Cookie.setPath(String uri)

    tex
    用来设置cookie的访问路径,以后浏览器必须访问这个路径或它的子路径,浏览器才将cookie的信息发送给服务器。

练习

java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/cookie_access")
public class CookieAccessServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置一个localhost:8080也能够发送的Cookie
        Cookie deep = new Cookie("deepPath", "deepCookieValue");
        deep.setPath("/");
        response.addCookie(deep);

        // 设置一个只在当前项目中才会被发送的Cookie
        Cookie context = new Cookie("contextPath", "contextCookieValue");
        context.setPath(request.getContextPath());
        response.addCookie(context);

        // 设置一个在当前项目中,指定的目录中(shallow),才会被发送的Cookie
        Cookie shallow = new Cookie("shallowPath", "shallowCookieValue");
        shallow.setPath(request.getContextPath() + "/shallow");
        response.addCookie(shallow);
    }

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

小结

把用户数据存储在浏览器的技术:Cookie

把用户数据存储在服务器的技术: Session

new Cookie("name", "value")

response.addCookie(cookie)

Cookie[] request.getCookies()

cookie.getName()

cookie.getValue()

URLEncoder.encode("字符串", "编码集")

URLDecoder.deocde("字符串", "编码集")

注意:编码和解码采用一致的编码集

过期时间:setMaxAge(秒)

立即销毁:setMaxAge(0)

setPath("uri")

案例

  • 使用 Cookie 技术,记录用户的上次访问时间

分析

Cookie-Demo-Analysis

实现

  • CookieUtils
java
package com.futureweaver.util;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

// Cookie相关工具类,用于查找Cookie
public class CookieUtils {
    // 根据cookie的name,查询cookie的value
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return null;
        }

        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(cookieName)) {
                return cookie.getValue();
            }
        }
        return null;
    }
}
  • AccessTimeServlet
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;

// 用户上次访问时间Demo(Cookie)
@WebServlet("/access_time")
public class AccessTimeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 因为在响应体里边有中文,所以先设置一下边编码
        response.setContentType("text/html; charset=utf-8");

        // "上次访问时间"的Cookie,定义名字为"visitTime"
        String visitTime = CookieUtils.getCookieValue(request, "visitTime");

        // 要往响应体里边写字符,所以先获取字符输出流
        PrintWriter out = response.getWriter();

        String now = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date());
        out.println("<H1>现在的时间是: " + now + "</H1>");
        if (visitTime != null) {
            out.println("<H1>您上次访问的时间是" + URLDecoder.decode(visitTime, "utf-8") + "</H1>");
        }

        // 要发送给浏览器"服务器当前时间"
        response.addCookie(new Cookie("visitTime", URLEncoder.encode(now, "utf-8")));
    }

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

小结

Cookie_Class_Hireachy

HttpSession

Market-Box

相关API概要

HttpSession_Class_Hireachy

HttpSession入门

API

  • HttpSession Request.getSession()
  • String HttpSession.getId()
    • 获取 Session 的 ID(唯一标识)(箱子的票号)
  • long HttpSession.getCreationTime()
    • 获取 Session 的创建时间
    • new SimpleDateFormate("yyyy-MM-dd HH:mm:ss").format(getCreationTime())
  • long HttpSession.getLastAccessedTime()
    • 获取 Session 的上次访问时间
  • boolean HttpSession.isNew()
    • 判断 Session 是否是新创建
  • ServletContext HttpSession.getServletContext()

练习

java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;

@WebServlet("/session_demo")
public class SessionDemoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();

        // 获取sesion
        HttpSession session = request.getSession();
        out.println("Session Id: " + session.getId() + "</BR>");
        out.println("Creation Time: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(session.getCreationTime()) + "</BR>");
        out.println("Last Accessed Time: " + new Timestamp(session.getLastAccessedTime()) + "</BR>");
        out.println("Is New: " + session.isNew() + "</BR>");
        out.println("Servlet Context: " + session.getServletContext() + "</BR>");

        // 设置最大非活动时间(每访问一次,即活动一次)
//        session.setMaxInactiveInterval(10);
    }

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

Session设置过期时间【重点】

API

  • HttpSession.setMaxInactiveInterval()

    tex
    单独设置Session的非活动(过期)时间(秒)
  • HttpSession.invalidate()

    tex
    立即销毁Session
  • web.xml【了解】

    xml
    <session-config>
        <!-- 分钟 -->
        <session-timeout>5</session-timeout>
    </session-config>

request.getSession()

session.setMaxInactiveInterval(秒)

session.invalidate();

练习

  • 用户登录后,设置会话 10 秒钟过期

  • 用户退出后,设置会话失效

  • LoginServlet.java

java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取session,这个session,和SessionServlet当中的session,是同一个
        HttpSession session = request.getSession();

        // 设置最大非活动时间
        session.setMaxInactiveInterval(10);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • SignOutServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/sign_out")
public class SignOutServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取session,这个session,和SessionServlet当中的session,是同一个
        HttpSession session = request.getSession();

        // 立即销毁session
        session.invalidate();
    }

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

getSession的内部活动

HttpSession_Creative_Activity

  1. 第 1 次用户访问没有会话,由服务器调用 s()创建一个会话对象。分配了一个会话 ID 给当前的用户。
  2. 创建一个 Cookie,名字叫:JSESSIONID,把会话 ID 发送给浏览器。
  3. 浏览器默认是浏览器关闭 Cookie 就过期,只要不关闭 Cookie 就会将会话 ID 发送给服务器
  4. 服务器得到上次的会话 ID,与内存中的会话比较,找到它的会话空间继续访问。
  5. 不同的用户,会话 ID 是不同的。

问题:Session不见了,会话丢了,什么原因?

  1. cookie 丢了
    1. 超时时间过了
    2. 浏览器关掉了,cookie 也不会见
  2. session 被销毁了
    1. invalidate
    2. setMaxInactiveInterval
    3. 配置时间到了

会话域【重点】

  • void HttpSession.setAttribute(String name, Object value)
    • 往域当中设置一个属性
  • Object HttpSession.getAttribute(String name)
    • 从域当中拿一个属性
  • void HttpSession.removeAttribute(String name)
    • 从域当中删除一个属性

案例

  • 使用 Session 技术,实现购物车功能

需求分析

  • index.html

    tex
    商品选择页
    选择商品后,提交至AddToCartServlet
  • AddToCartServlet.java

    tex
    将当前用户挑选的商品,添加至购物车
  • BalanceServlet.java

    tex
    展示当前用户挑选的商品

实现

  • index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="add_to_cart" method="post">
    华为手机<input type="checkbox" name="good" value="华为手机"/><br/>
    小米手机<input type="checkbox" name="good" value="小米手机"/><br/>
    ThinkPad<input type="checkbox" name="good" value="ThinkPad"/><br/>
    中兴手机<input type="checkbox" name="good" value="中兴手机"/><br/>
    <input type="submit" value="提交"/>
</form>

</body>
</html>
  • AddToCartServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

// 把用户提交上来的商品,添加到购物车里面
// 用户勾选的商品,在参数里边,参数的名字是good
// 用户的购物车,使用session技术进行存储
// sesion作用域存储的数据是键值对的,键值对当中的键,叫"goods"
// session goods所对应的值是List类型
@WebServlet("/add_to_cart")
public class AddToCartServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置请求体和响应体的字符编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html; charset=utf-8");

        // 获取用户提交上来的商品
        String[] paramGoods = request.getParameterValues("good");

        // 获取session
        HttpSession session = request.getSession();

        // 先要获取这个用户之前的购物车里边放的List
        List<String> goods = (List<String>) session.getAttribute("goods");

        // 因为有可能用户第一次访问,那么session作用域当中,有可能没有任何的数据
        // 没有任何数据的话,应该给用户创建一个新的List
        if (goods == null) {
            goods = new ArrayList<>();
        }

        // 因为用户可能直接请求到了这个Servlet,不从html过来,那么good这个参数就会获取一个空
        // 需要先判断空指针校验
        if (paramGoods != null) {
            // 将一个数据转换成List
            List<String> strings = Arrays.asList(paramGoods);

//            // 因为第二次请求时,把这个strings直接覆盖掉了,所以要处理
//            session.setAttribute("goods", strings);
            goods.addAll(strings);
        }

        // 因为用户可能第一次请求,所以这个对象,要重新设置回去
        session.setAttribute("goods", goods);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • BalanceServlet.java
java
package com.futureweaver.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

// 将用户购物车里面的商品,读出来,然后放到响应里边
// 因为购物车是放到了session当中,所以获取购物车里边的商品,就从session当中,去读
// 定义好的:session当中的对象名字是goods
// 定义好的:session当中的对象的值是List<String>
@WebServlet("/balance")
public class BalanceServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html; charset=utf-8");

        PrintWriter out = response.getWriter();

        // 因为要调用session的getAttribute方法,所以先拿到一个session
        HttpSession session = request.getSession();

        List<String> goods = (List<String>) session.getAttribute("goods");

        // 因为用户可能直接来访问结算,所以还需要做一遍空指针校验
        if (goods != null) {
            out.println("<h1>您好,以下是您在购物车当中的商品</h1>");

            for (String good : goods) {
                out.println("<h3>"+good+"</h3>");
            }
        }
    }

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

Servlet三个作用域总结

  • 作用域对象的获得方式
作用域获得方式
请求域request参数
会话域request.getSession()
上下文域getServletContext()
request.getServletContext()
session.getServletContext()
  • 作用域的生命周期
作用域接口名作用范围生命周期
请求域HttpServletRequest1个用户的1个请求服务器接收到浏览器的访问时创建
响应结束时销毁
会话域HttpSession1个用户的所有请求用户第一访问时被创建
会话(session)过期
上下文域ServletContext所有用户的所有请求项目加载开启时创建
项目卸载关闭时销毁
  • 三个作用域共同的方法
功能方法
存放数据setAttribute(String name, Object value);
获取数据Object getAttribute(String name)
删除数据removeAttribute(String name)

小结

  • 相关 API

HttpSession_Class_Hireachy

getCreationTime

获取Session的创建时间(1970-01-01至今的毫秒数)

getId

箱子的票号

getLastAccessedTime

获取上一次的访问时间(1970-01-01至今的毫秒数)

setMaxInactiveInterval

设置Session的最大非活动时间

每一次浏览器进行访问时,就相当一次活动

invalidate

立即销毁Session

isNew

Session是否是新创建的

getAttribute

获取数据

setAttriburte

设置(存放)数据

removeAttribute

删除数据

  • getSession 的内部活动

HttpSession_Creative_Activity

  • HttpSession 是一个域对象
  • 三个作用域的获得、生命周期
    • Request 的创建、销毁

      一个用户的一次请求

    • Session 的创建、销毁

      一个用户的所有请求

    • ServletContext 的创建、销毁

      所有用户的所有请求

学习目标总结

  • 能够理解会话技术
  • 能够应用 request 和 response 对象操作 cookie
  • 能够应用 cookie 对象的方法
  • 能够应用 cookie 完成显示用户上一次访问时间的案例
  • 能够应用 session 对象的方法
  • 能够理解域对象的含义