Cookie & Session

147 阅读6分钟

会话技术

概述

用户打开一个浏览器,点击多个超链接访问服务器的web资源,然后关闭浏览器,整个过程称为会话。每个用户与服务器交互都会产生各自的数据,保存这些数据需要使用会话技术。

为何不能用Request或者ServletContext:

image.png

会话技术分类

  • Cookie(服务器保存到客户端的键值对)
    • 客户端的技术
    • 把每个用户的数据以cookie的形式保存到浏览器中;当用户使用浏览器再次访问服务器中web资源时,会带着各自cookie过去。这样web资源处理的就是各自的数据
  • Session
    • 服务器端的技术
    • 服务器在运行时为每一个浏览器用户创建一个独享的session对象;用户访问服务器时,可以把各自数据放入各自的session中或者取用各自session中的数据

Cookie实现原理

image.png

Session实现原理

image.png

用Cookie记录上次访问时间

需求分析:

image.png

代码实现:(Cookie的简单使用)

HttpServletRequest - Cookies[] getCookies() -- 返回浏览器带回来的所有cookie

HttpServletResponse - void addCookie(Cookie cookie) -- 向浏览器回写cookie

Cookie - Cookie(String name, String value) -- 构造Cookie

package com.servlet.utils;

import javax.servlet.http.Cookie;

/**
 * 查找指定名称Cookie的工具类
 */
public class CookieUtils {

    public static Cookie findCookie(Cookie[] cookies, String name) {
        if (cookies == null) {
            return null;
        } else {
            for (Cookie cookie : cookies) {
                if (name.equals(cookie.getName())) {
                    return cookie;
                }
            }
            return null; // 没有指定名称的Cookie情况
        }
    }
}
package com.servlet.demo;

import com.servlet.utils.CookieUtils;

import javax.servlet.ServletException;
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.text.SimpleDateFormat;
import java.util.Date;

public class VisitServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         * 用户访问Servlet,第一次访问,显示欢迎,记录当前访问时间,存入cookie,回写到浏览器
         * 非第一次访问,从cookie中获得上次的时间,显示到页面;再记录当前访问时间,存入cookie,回写到浏览器
         */
        // 判断是否第一次访问
        Cookie[] cookies = req.getCookies();
        Cookie lastVisit = CookieUtils.findCookie(cookies, "lastVisit");
        if (lastVisit == null) {
            // 第一次访问
            resp.setContentType("text/html;charset=UTF-8");
            resp.getWriter().println("<h3>Welcome</h3>");
        } else {
            // 非第一次访问,获得上一次访问时间并显示
            String value = lastVisit.getValue();
            resp.setContentType("text/html;charset=UTF-8");
            resp.getWriter().println("<h3>您上次访问的时间是:" + value + "</h3>");
        }
        // 记录当前的系统时间,存入cookie,回写浏览器
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
        Cookie cookie = new Cookie("lastVisit", sdf.format(date));
        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

用Session保存用户登录信息

login.jsp

<%--登录表单,并显示错误信息--%>
<%--登陆表单包括用户名和密码,表单绝大部分指向LoginServlet--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>请登录!</h2>
<%
    String uname = "";
    // 读取cookie:uname
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if ("uname".equals(cookie.getName())) {
                uname = cookie.getValue();
            }
        }
    }
%>
<%
    String message = "";
    String msg = (String) request.getAttribute("message");
    if (msg != null) {
        message = msg;
    }
%>
<font color="red"><%= message %>
</font>
<form action="/web_test3_war_exploded/LoginServlet" method="post">
    用户名:<input type="text" name="username" value="<%=uname%>"> <br/>
    密 码:<input type="text" name="password"> <br/>
    <input type="submit" value="点击登录">
</form>
</body>
</html>

LoginServlet.java

/**
 * 用来获取表单数据并且校验用户名和密码是否正确,错误时可以显示错误信息
 * 正确时,保存用户信息到 session 中,重定向到 succ1.jsp
 * 错误,保存错误信息到 request 域,转发回到 login.jsp
 * 判断用户名与密码是否正确可自己设定
 */
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        // 校验
        if ("root".equals(username) && "root123".equals(password)) {
            // 用户名保存到cookie中
            Cookie cookie = new Cookie("uname", username);
            cookie.setMaxAge(60 * 60 * 24);
            resp.addCookie(cookie);
            // 保存用户信息到 session 中
            HttpSession session = req.getSession();
            session.setAttribute("username", username);
            session.setAttribute("password", password);
            // 重定向到 succ1.jsp
            resp.sendRedirect("/web_test3_war_exploded/session/succ1.jsp");
        } else { //登录失败
            // 保存错误信息到 request 域
            req.setAttribute("message", "您的用户名或密码错误");
            // 转发回到 login.jsp
            req.getRequestDispatcher("/session/login.jsp").forward(req, resp);
        }
    }
}

succ1.jsp

<%--
    成功页面1 succ1.jsp (登录后的用户可以访问)
    从session中获取用户信息,
    如果存在,说明已经登录显示用户名;
    如果不存在,向 request 域保存错误信息,转发到login.jsp
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>succ1.jsp</h2>
<%
    String username = (String) session.getAttribute("username");
    if (username == null) {
        // 向 request 域保存错误信息,转发到login.jsp
        request.setAttribute("message", "您没有登录成功,请重新登录!");
        request.getRequestDispatcher("/session/login.jsp").forward(request, response);
        return;
    }
%>
欢迎<%= username %>登录本网站!
</body>
</html>

Cookie

Cookie的分类

  • 默认级别的Cookie
    • 指的是没有设置有效时间的Cookie,存在于浏览器的内存中
    • 默认情况下只要关闭了浏览器,Cookie就会被销毁
  • 持久级别的Cookie
    • 指的是有有效时间的Cookie,持久化保存到硬盘上
    • 再次打开浏览器时,会加载硬盘上的文件,数据就一直存在

常用API

  • 构造方法:
    • Cookie(String name, String value)
  • 成员方法:
    • String getName() 获得Cookie的名称
    • String getValue() 获得Cookie的值
    • void setDomain(String pattern) 设置Cookie的有效域名
    • void setPath(String uri) 设置Cookie的有效路径
    • void setMaxAge(int expiry) 设置Cookie的有效时长(单位是秒。设置为0就是删除)
image.png

一些使用细节

  • 一个Cookie只能标识一种信息,至少含有一个标识该信息的名称和值
  • 一个web网站可以给一个浏览器发送多个Cookie,一个web浏览器可以存储多个web站点的Cookie
  • 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4k(老版的浏览器)。新版一般扩容了
  • 如果创建了一个Cookie并发送到了浏览器中,默认情况下是一个会话级别的Cookie。用户退出浏览器,就被删除了。
  • 如果希望将Cookie存到硬盘上,那么需要设置有效时长
  • 需要手动删除持久性Cookie,可以将Cookie的有效时长设置为0。需要注意删除的时候,path必须一致,否则无法删除

Session

  • Session也是一次会话,Cookie是将数据保存到浏览器中,Session是将数据保存到服务器端。
  • 注意:一个浏览器会独占一个Session对象。
  • 在需要保存用户数据时,写入到Session对象中,使用浏览器访问其他程序时,可以从Session中取出该用户的数据。
  • 特点:(1)没有个数和大小的限制;(2)数据保存在服务器上,相对安全;

Session保存用户数据

Session对象由服务器创建,调用request对象的getSession()方法得到Session对象

image.png

Session实现原理

基于Cookie的,基于Cookie回写了Session的ID

image.png

Session作为域对象存取数据

HttpSession API:

  • void setAttribute(String name, Object value) 往Session中存入数据
  • Object getAttribute(String name) 从Session域中获取数据
  • void removeAttribute(String name) 从Session域中移除数据

Session作为域对象的作用范围:

一次会话的范围;指的是用户打开浏览器,点击多个超链接访问服务器资源,最后关闭浏览器的过程

Servlet数据访问范围总结

Servlet域对象创建时机销毁时机存数据取数据作用范围
请求范围(ServletRequest)当用户向服务器发送一次请求,服务器就会创建一个request对象当服务器对这次请求做出了响应,服务器就会销毁这个request对象使用void setAttribute(String name, Object value)方法使用Object getAttribute(String name)方法一次请求(转发就是一次请求)
会话范围(HttpSession)服务器端第一次去调用getSession()方法的时候,就会创建一个Session对象三种情况:(1)Session过期(默认时间是30分钟)(2)非正常关闭服务器(正常关闭时Session会被序列化)(3)手动调用Session中的invalidate()方法使用void setAttribute(String name, Object value)方法使用Object getAttribute(String name)方法一次会话(多次请求)
应用范围(ServletContext)-全局范围服务器启动的时候,为每个web项目创建一个单独的ServletContext对象服务器关闭的时候,或web项目从服务器移除的时候使用void setAttribute(String name, Object value)方法使用Object getAttribute(String name)方法- - - 整个应用的范围