自学内容网 自学内容网

【JavaEE】Cookie 和 Session

一、Cookie 是什么?

HTTP 协议自身是属于 “无状态” 协议

“无状态” 的含义指的是:默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系。

但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的。例如登陆网站成功后, 第二次访问的时候服务器就能知道该请求是否是已经登陆过了。

在这里插入图片描述

图中的 “令牌” 通常就存储在 Cookie 字段中

  1. 到了医院先挂号. 挂号时候需要提供身份证, 同时得到了一张 “就诊卡”, 这个就诊卡就相当于患者的 “令牌”.
  2. 后续去各个科室进行检查, 诊断, 开药等操作, 都不必再出示身份证了, 只要凭就诊卡即可识别出当前患者的身份.
  3. 看完病了之后, 不想要就诊卡了, 就可以注销这个卡. 此时患者的身份和就诊卡的关联就销毁了. (类似于网站的注销操作)
  4. 又来看病, 可以办一张新的就诊卡, 此时就得到了一个新的 “令牌”

此时在服务器这边就需要记录令牌信息,以及令牌对应的用户信息,这个就是 Session 机制所做的工作。

二、理解会话机制(Session)

服务器同一时刻收到的请求是很多的. 服务器需要区分清楚每个请求是从属于哪个用户, 就需要在服务器这边记录每个用户令牌以及用户的信息的对应关系.

在上面的例子中, 就诊卡就是一张 “令牌”. 要想让这个令牌能够生效, 就需要医院这边通过系统记录每个就诊卡和患者信息之间的关联关系.

会话的本质就是一个 “哈希表”, 存储了一些键值对结构. key 就是令牌的 ID(token/sessionId), value 就是用户信息(用户信息可以根据需求灵活设计).

sessionId 是由服务器生成的一个 “唯一性字符串”, 从 session 机制的角度来看, 这个唯一性字符串称为 “sessionId”. 但是站在整个登录流程中看待, 也可以把这个唯一性字符串称为"token"。
sessionId 和 token 就可以理解成是同一个东西的不同叫法(不同视角的叫法).

  1. 当用户登陆的时候, 服务器在 Session 中新增一个新记录, 并把 sessionId / token 返回给客户端. (例如通过 HTTP 响应中的 Set-Cookie 字段返回).
  2. 客户端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId/ token. (例如通过 HTTP 请求中的 Cookie 字段带上).
  3. 服务器收到请求之后, 根据请求中的 sessionId / token 在 Session 信息中获取到对应的用户信息, 再进行后续操作.

Servlet 的 Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失

三、Cookie 用来做什么

Cookie 是浏览器给 HTTP 协议提供的一个持久化方案
Cookie 存储的是键值对,不能存复杂的对象,只能存字符串
Cookie 是按照域名来进行分类存储的

从哪来: 数据来自于服务器.服务器代码想让浏览器存啥,就返回啥.通过在HTTP响应报文中 header加入Set-Cookie
到哪去:回到服务器去.每次给服务器发送的HTTP请求,就会带上之前存储的Cookie信息(在请求 header 中带有 Cookie 属性)

服务器:session 保存用户详细信息,键值对保存,key-value,key 是服务器生成的唯一字符串,也就是sessionId,服务器把这个键(sessionId)通过 Set-Cookie 返回给浏览器。那么以后的请求中Cookie就带有sessionId。

实现登录和身份验证,是 Cookie 的一种典型应用,不是所有应用。
Cookie 里面存啥都行,只不过在身份验证的场景中,会存储 sessionId。
Cookie 里面可以存储多个键值对,比如上次登录时间,用户的一些临时信息。
只是在实现身份验证的时候需要 Cookie-Session 搭配使用,其他的的时候不一定需要使用Session。

四、Cookie 和 Session 的区别

  • Cookie 是客户端的机制. Session 是服务器端的机制.
  • Cookie 和 Session 经常会在一起配合使用. 但是不是必须配合.
    • 完全可以用 Cookie 来保存一些数据在客户端. 这些数据不一定是用户身份信息, 也不一定是 token / sessionId
    • Session 中的 token / sessionId 也不需要非得通过 Cookie / Set-Cookie 传递.

五、核心方法

Servlet 对 Cookie 和 Session 提供的API

HttpServletRequest 类中的相关方法

方法描述
HttpSession getSession()在服务器中获取会话,参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null
Cookie[] getCookies()返回一个数组,包含客户端发送该请求的所有的 Cookie 对象,会自动把Cookie 中的格式解析成键值对

在这里插入图片描述

HttpServletResponse 类中的相关方法

方法描述
void addCookie(Cookie cookie)把指定的 cookie 添加到响应中

在响应报文中添加 Cookie,想让浏览器的Cookie存什么,就通过这里写进去,这里的内容会放到响应报文的Set-Cookie 字段里面。

HttpSession 类中的相关方法
一个 HttpSession 对象里面包含多个键值对. 我们可以往 HttpSession 中存任何我们需要的信息

方法描述
Object getAttribute(String name)该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null
void setAttribute(String name, Object value)该方法使用指定的名称绑定一个对象到该 session 会话
boolean isNew()判定当前是否是新创建出的会话

Cookie 类中的相关方法
每个 Cookie 对象就是一个键值对.

方法描述
String getName()该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 SetCooke字段设置给浏览器的)
String getValue()该方法获取与 cookie 关联的值
void setValue(String newValue)该方法设置与 cookie 关联的值

六、用户登录

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 用form来构造一个post请求,把用户名和密码通过body键值对的形式传过去,此处login为相对路径 -->
    <form action="login" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" name="提交">
    </form>
</body>
</html>

IndexServlet.java

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("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 把用户的信息展示到页面上
        HttpSession session = req.getSession(false);
        if (session == null) {
            // 用户未登录,调转到登录页面
            resp.getWriter().write("login.html");
            return;
        }
        String username = (String) session.getAttribute("username");
        String password = (String) session.getAttribute("password");
        Integer visitCount = (Integer) session.getAttribute("visitCount");
        visitCount += 1;
        session.setAttribute("visitCount", visitCount);

        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("username: " + username + " 访问次数: " + visitCount);
    }
}

LoginServlet.java

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

// 用来处理登录请求
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || username.equals("") || password == null || password.equals("")) {
            // 参数不完整,直接返回到登录页面
            resp.getWriter().write("login.html");
            return;
        }
        if (!username.equals("zhangsan") || !password.equals("1234")) {
            // 登录失败
            resp.getWriter().write("用户名或密码错误,登录失败");
            return;
        }
        // 登录成功
        // 创建一个会话,将用户名和密码填写进去
        HttpSession session = req.getSession(true);
        session.setAttribute("username", "zhangsan");
        session.setAttribute("password", "1234");

        Integer visitCount = (Integer) session.getAttribute("visitCount");
        if (visitCount == null) {
            session.setAttribute("visitCount", 0);
        }

        // 让页面跳转到登录页面, 重定向跳转是 get 请求
        resp.sendRedirect("index");
    }
}

原文地址:https://blog.csdn.net/2301_79765510/article/details/143872852

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!