Cookie 和 Session 工作流程

发展史

关于 cookie、session与token的真正区别 引用:东北一绝、英俊侠作者的文章。

Cookie 和 Session 工作流程

基于以上关于:在 HTTP 前后端交互中由于 HTTP 是 “无状态协议”,而在实际用户使用浏览器时不单单为了查询信息 / 资源,而是需要进行一些相关身份验证(登录状态),而又不能在每跳转到一个页面都要重新进行验证~ 于是在我们的服务器就需要储存的一些 信息 和 状态 的记录 而发展的 Cookie 和 Session 机制,对标于我们的一个具体的 Servlet 项目,以下是对相关 API 和 工作流程 的详细介绍。

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

在基于 Servlet 框架的使用 HTTP 进行前后端交互项目中,对于 Cookie 和 Session 都有很好的支持,可以基于相关类提供的 API 来对我们自己的项目进行会话管理的操作。

Servlet 提供的关键 API

HttpServletRequest 类中的相关方法

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

getSession()方法:

既能用于获取到服务器上的会话,也能用于创建会话。

如果参数为 true:会话不存在,则创建会话;会话存在,则获取。(第一次登录的时候)

如果参数为 false:会话不存在,则返回 null;会话存在,则获取。(判断登陆状态的时候)

在调用 getSession 的时候需要做的事情:

  • 创建会话
  1. 首先先获取到请求中 cookie 里面的 sessionId字段,相当于会话的身份标识
  2. 判定这个 sessionId 是否在当前服务器上存在。
  3. 如果不存在,则进入创建会话逻辑:
    会创建一个 HttpSession 对象,并且生成一个 sessionId(是一串很长的十六进制数,保证唯一性)
    接下来就会把这个 sessionId 作为 key,把这个 HttpSession 对象作为 value,把这个键值对保存到服务器内存的一个“哈希表”这样的结构中。
    最后,服务器就会返回一个 HTTP 响应,把 sessionId 通过 Set-Cookie 字段返回给浏览器。浏览器就可以保存这个 sessionId 到 Cookie 中了。
  • 获取会话
  1. 先获取到请求中的 cookie 里面的 sessionId 字段(也就是会话的身份标识)
  2. 判定这个 sessionId 是否在当前服务器上存在(也就是在这个"哈希表"中是否有)
  3. 如果有,就直接查询出这个 HttpSession 对象,并且通过返回值返回回去。

getCookies()方法:

获取到请求中的 Cookie 数据,返回值是 Cookie 类型的数组,每个元素是一个 Cookie 对象。每个 Cookie对象又包含了两个属性:name,value (还是键值对)。

HTTP 请求中的 cookie 字段就是按照键值对的方式来组织的,这些键值对都会在请求中通过 cookie 字段传给服务器,服务器收到请求之后,就会进行解析,解析成 Cookie[] 这样的形式来返回。

HttpServletResponse 类中的相关方法

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

响应中就可以根据 addCookie 这个方法来添加一个 Cookie 信息到响应报文中,这里添加进来的键值对,就会作为 Http 响应中的 Set-Cookie 字段来表示。

HttpSession 类中的相关方法

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

这个对象本质上也是一个“键值对”结构,允许程序员往 HttpSession 对象中储存任意的键值对结构(key 必须是 String,value 是一个 Object)

Cookie 和 Session 工作流程_第1张图片

  1. HttpSession 对象里面包含了若干键值对,里面的 key 和 value 都是程序员自定义的。
  2. HttpSession 里面的每个键值对,称为 属性(Attribute).。
  3. HeepSession 提供了两个方法:
    getAttribute :取键值对。
    setAttribute :存键值对。

Cookie 类中的相关方法

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

补充用法注意事项:

Cookie 这里是可以保存任意的键值对,包括用户自定义的键值对,所以在获取键值对时,如果是一般的键值对,直接通过 getCookies 来获取。如果是特殊的键值对(保存 sessionId 的键值对),直接用 getSession 来自动的帮我们从 Cookie 中取 sessionId 了。

实现登录案例交互:

图例分析:
Cookie 和 Session 工作流程_第2张图片
约定前后端交互接口:

  • 登录 + 获取主页
    Cookie 和 Session 工作流程_第3张图片
  1. 编写一个简单的登陆页面:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录系统title>
    <script src="js/jquery-1.9.1.min.js">script>
head>
<body>
<div style="margin-top:50px;margin-left:40%;">
    <h1 style="padding-left:50px;">用户登录h1>
    姓名:<input id="username" type="text" >
    <p>
        密码:<input id="password"  type="password" >
    <p>
    <div style="padding-left:50px;">

        <input type="button" value=" 提 交 " onclick="mySubmit()">    
        <input type="reset" value=" 重 置 ">
    div>
div>
<script>
    // ajax 请求登录
    function mySubmit(){
        // 非空效验
        var username = jQuery("#username");
        var password = jQuery("#password");
        if(""==jQuery.trim(username.val())){
            alert("请先输入用户名!");
            username.focus();
            return;
        }
        if(""==jQuery.trim(password.val())){
            alert("请先输入密码!");
            password.focus();
            return;
        }
        jQuery.ajax({
            url:"login", // 设置请求地址
            type:"POST", // 设置请求方法类型
            contentType:"application/x-www-form-urlencoded; charset=utf-8", // 请求类型
            // dataType:"", // 响应的类型
            data:{"username":username.val(),"password":password.val()}, // 请求参数
            success:function(data){
                alert(data);
            }
        });
    }
script>
body>
html>
  1. 登录逻辑
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 {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理用户请求
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        // 判定用户名或者密码是否正确~~
        // 正常来说这个判定操作是要放到数据库中进行存取的.
        // 此处为了简单, 就直接在代码里写死了. 假设有效的用户名和密码是 "admin", "123"
        if ("admin".equals(username) && "123".equals(password)) {
            // 登录成功!
            // 创建会话, 并保存必要的身份信息.
            HttpSession httpSession = req.getSession(true);
            // 往会话中存储键值对. 必要的身份信息
            httpSession.setAttribute("username", username);
            // 初始情况下, 把登录次数设为 0
            httpSession.setAttribute("count", 0);
            resp.sendRedirect("index");
        } else {
            // 登录失败!
            resp.getWriter().println("login failed!");
        }
    }
}
  1. 主页逻辑
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 {
        // 返回一个主页. (主页就是一个简单的 html 片段)
        // 此处需要得到用户名是啥, 从 HttpSession 中就能拿到.
        // 此处 getSession 的参数必须是 false. 前面在登录过程中, 已经创建过会话了. 此处是要直接获取到之前的会话.
        HttpSession session = req.getSession(false);
        String username = (String) session.getAttribute("username");
        // 还从会话中取出 count.
        Integer count = (Integer) session.getAttribute("count");
        count += 1;
        // 把自增之后的值写回到会话中.
        session.setAttribute("count", count);

        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("

欢迎你! " + username + " 这是第 " + count + " 次访问主页

"
); } }

Cookie 和 Session 工作流程_第4张图片

Servlet 内部会帮我们去 维护这样相同的 sessionId 对应的对象,我们只需要根据业务需要进行对应的传参就好了。

Cookie 和 Session 工作流程_第5张图片
交互成功~

你可能感兴趣的:(网络,http,Servlet)