Cookie和Session原理详解

news/2024/12/29 15:07:52/

目录

前言

Session

会话机制

Cookie和Session的区别

Servlet中对Session和Cookie的封装

代码实例:实现用户登录

约定前后端交互的接口

前端页面:

后端实现

login

index

总结



前言

在web的发展史中,我们知道浏览器和服务器交互用的是http协议,但是http协议是一种无状态的协议,而这就将导致服务器并不知道是那个浏览器在访问服务器,这就将会出现了很大的问题,所以一些网页需要知道用户的状态,比如网站登录,购物车等等。为了解决上述的问题,Cookie和Session就出现了。

Cookie

Cookie中存储了一个字符串,这个字符串是key-value形式的,这个cookie一般来自服务器,服务器通过的set-cookie字段将cookie返回给浏览器。当服务器通过set-cookie把cookie返回给浏览器之后,服务器就相当于记录下来了当前用户的状态,当再次访问服务器的其他页面是,浏览器的请求报文了就会携带服务器上次返回的cookie字段。服务器看到这个字段,也就知道了当前是那个用户在进行浏览网页。

  • cookie是从服务器来
  • cookie是到服务器去
  • cookie的作用就是保存当前用户的状态。

cookie的作用就是在浏览器本地存储数据的一种机制。

cookie最典型的应用就是保存用户的身份信息。

上述的令牌就是cookie。

Session

Session(会话)就是服务器这边用于区分用户身份的一种机制,通常是配合cookie来使用的。

例如:当我们第一次登录一个网站的时候,浏览器给服务器发送的请求报文了并没有cookie字段,如果我们登录成功,服务器这边就会给登录成功的用户分配一个sessionId,同时记录下来当前用户的一些身份信息。然后浏览器会通过响应包中的set-cookie字段把sessionId给浏览器传递过去,后续浏览器再次访问这个网站的时候,请求包中会一直携带这个sessionId,这个sessionId就是浏览器给服务器发送的cookie字段。从而能够让服务器识别当前用户的身份了。

会话机制

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

Servlet中Session是保存在内存中的,如果重新启动,Session中的数据就会丢失。

Cookie和Session的区别

  • Cookie是客户端的一种用于存储数据的机制,是键值对结构,可以存储用户的信息,也可以存储关键的信息,关于怎么存储,这些都是程序员自定义的。
  • Session是服务器端存储数据的一种机制,也是键值对结构,主要存储用户的相关信息。
  • Cookie和Session通常搭配到一起使用,但是也不是必须要一起使用。

Servlet中对Session和Cookie的封装

核心方法

HttpServletRequest:

HttpSession  getSession();在服务器获取会话,参数是boolean值,如果true,则当不存在时创建新的会话,参数如果是false,则当不存在时返回null。
Cookie[] getCookies();

返回一个数组,包含客户端发送该请求的所有Cookie对象,自动把Cookie中的格式解析为键值对结构。

实际开发中,使用getCookies() 的场景非常少,主要还是getSession()方法。

我们主要来看看getSession 方法:

这个方法在使用时需要传入一个boolean值:

当我们传入的时true时,首先会读取请求中cookie里面的sessionID 在服务器这边根据sessionID来查询对于的session对象 如果查询到了,就直接返回这个session对象, 如果没有查询到,就会才创建一个session对象,同时生成一个sessionID, 以sessionID为key,以session对象为value,把这个键值对存储在服务器里面的一个hash表中 同时把sessionID以set-Cookie的方法返回给浏览器。

当我们传入的是false时,读取请求中cookie里面的sessionID ,在服务器这边根据sessionID来查询对于的session对象 ,如果查询到了,就直接返回这个session对象,如果没有查询到,就返回一个null。

代码实例:实现用户登录

我们通过实现用户登录的例子来综合的理解Cookie和Session。

约定前后端交互的接口

POST方法提交数据   路径为login

提交的数据格式为form表单的形式  Content-Type:application/x-www-form-urlencoded

例如username=zhangsan&password=123

一般向这样的登录请求,都是使用post方法比较对。属于是使用习惯。

首先要实现一个用户登录的场景,我们先要得到前端的页面。

前端页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录</title>
</head>
<body><form action="login" method="post"><input type="text" name="username"><input type="password" name="password"><input type="submit" value="登录"></form></body>
</html>

可以看到上述代码中,我们是以form表单的形式提交的数据,方法是post,路径为login。

页面效果:

 

 当我们点击登录的时候,就会把我们输入框里面的用户名和密码以form表单的形式通过post方法向路径为login提交数据。

如果用户名和密码正确,我们就跳转到一个新的页面,如果不正确,我们就提交用户名或密码错误。

后端实现

我们需要先创建一个Servlet,用于处理前端提交的数据。

login

package index;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 Login extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//为了保证读出来的用户名和密码也支持中文,需要设置编码方式为utf8req.setCharacterEncoding("utf8");//拿到用户名和密码String username = req.getParameter("username");String password = req.getParameter("password");//验证用户名和密码//假定username和password为zhangsan,lisi 和123if (username == null || password == null || username.equals("") || password.equals("")) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("用户名和密码不能为空");return;}if(!(username.equals("zhangsan") || username.equals("lisi"))) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("用户名或密码错误");return;}if(!password.equals("123")) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("用户名或密码错误");return;}//创建一个会话HttpSession session = req.getSession(true);//让刚刚创建好好的session对象存储我们自定义的数据,就可以在这个对象中存储用户的身份信息session.setAttribute("username", username);//session对象本身可以视为一个哈希表   key就是String类型,value是object类型//用于存放访问index页面的次数session.setAttribute("loginCount",0);//重定向到index页面resp.sendRedirect("index");}
}

这个Servlet主要做的事情就是验证前端发送的数据是否合法,如果不合法,就发出提示,如果合法,就通过getSession创建出来一个回话,可以看出,我们设置的参数为true,这个getSession方法首先会进行读取前端发送的Cookie里面的SessionId,同时根据这个SessionId 来查询对应的session对象。如果查询到了,就返回这个对象,如果没有查询到,就创建出来一个新的Session对象,同时生成一个SessionId,以sessionID为key,以session对象为value,把这个键值对存储在服务器里面的一个hash表中 同时把sessionID以set-Cookie的方法返回给浏览器。

然后我们就可以在session对象中存储我们想要存储的数据了,我们存储的是用户的身份信息。

此时我们可以视为登录成功,我们就可以访问登录成功之后的主页了。

index

package index;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 index extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//此处的session对象和刚刚登录创建的session是同一个对象,因为是同一个sessionID//因为刚刚登录成功,就通过set-cookie返回给浏览器,下次访问index的时候,就会带上这个sessionID,//拿着这个sessionID再一查,就会查到一个session对象//此处禁止创建会话,如果没有找到,认为用户是未登录的状态//如果找到了,才认为是登录状态HttpSession session = req.getSession(false);if(session == null) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("用户未登录");resp.sendRedirect("login.html");return;}//由于session对象存储的hash表结构的数据,我们这里通过key(username)获取到valueString username = (String)session.getAttribute("username");if(username == null) {//虽然创建了对象,但是里面没有必要的属性,也认为是登录异常resp.setContentType("text/html;charset=utf8");resp.getWriter().write("用户未登录");resp.sendRedirect("login.html");return;}//如果上述检查都没有问题,下面就直接生成一个动态页面resp.setContentType("text/html;charset=utf8");//需要显示在页面上的访问次数  由于是object类型,需要强制转换为intint count = (int)session.getAttribute("loginCount");//int count1 = Integer.parseInt(count);count+=1;   //访问次数+1session.setAttribute("loginCount",count);resp.getWriter().write("欢迎你"+username+"<br>");resp.getWriter().write("访问次数为"+count);}
}

可以看出我们这次getSession里面的参数是false,功能就是读取请求中cookie里面的sessionID , 在服务器这边根据sessionID来查询对于的session对象 ,如果查询到了,就直接返回这个session对象,如果没有查询到,就返回一个null。也就是说此处是禁止创建会话的。

 下面我们通过浏览器进行访问。然后通过fiddler抓包软件进行抓包。

我们在浏览器中输入访问路径后然后输入用户名和密码

点击登录。

此时看看我们的抓包结果 :

此处的请求为post请求,路径为login,提交数据格式和我们前面约定的格式一致。

这个包中并没有Cookie字段。因为此时服务器还不知道是否用户名和密码正确,当用户名和密码正确之后,服务器才会创建Session对象,同时在响应中sessionId发送给浏览器。

接下来我们看看响应:

可以看出在相应包里面有一个Set-cookie字段。

 这里的数据就是服务器在验证用户名和密码都正确之后,创建的Session对象的sessionId,然后把这个sessionId返回给浏览器,后续浏览器在访问的时候就会通过Cookie携带这个sessionId,服务器就会根据这个sessionId来查询对应Session对象。如果没有查询到,就会返回一个null。

此时页面成功的跳转到了index页面,也就是说明此时成功登录。 

 在抓包结果里面我们就发现了Cookie字段,而这个Cookie字段里面的值和第一个登录时服务器给浏览器返回的set-Cookie里面的SessionId的值一致。服务器就是通过这个sessionId来区分不同的用户的。服务器也就知道是那个用户在访问页面了。

下面我们以别的用户登录再次来加深印象。

我们通过用户名为lisi,密码为123来登录,再次看效果。

 

可以看出,服务器在创建Session对象之后,通过Set-Cookie字段把SessionId返回给浏览器。

在浏览器登录成功之后,访问主页的时候,请求包中就会携带Cookie字段,里面的值就是SessionId,服务器此时也就知道是那个用户在访问页面了。 

总结

Cookie和Session浏览器和服务器传输数据的一种方法,但是需要注意的是,Cookie是有长度限制的,并不能存储比较大的数据,只能存储一些关键的信息。结合上述的例子,再来看这个图就会清晰很多。


http://www.ppmy.cn/news/241353.html

相关文章

小程序自动化测试

背景 近期团队打算做一个小程序自动化测试的工具&#xff0c;期望能够做到业务人员操作一遍小程序后&#xff0c;自动还原之前的操作路径&#xff0c;并且捕获操作过程中发生的异常&#xff0c;以此来判断这次发布是否会影响小程序的基础功能。 上述描述看似简单&#xff0c;…

读财报丨Q1保费环比增长33.4%,慧择增长源泉来自于何处?

一季度我国经济表现开局良好&#xff0c;保险行业增长态势明朗。从财报来看&#xff0c;中国人寿、中国平安、中国人保、新华保险、中国太保等大型上市险企Q1净利润纷纷超预期&#xff0c;随着巨头业绩转暖&#xff0c;保险中介行业也迎来了发展好时机。 近日&#xff0c;国内…

centos7安装jdk8

准备&#xff1a;centos7环境&#xff0c;并且能链接网络配置好yum源 打开终端&#xff0c;并以root用户身份登录或使用sudo权限。 1、在终端中运行以下命令&#xff0c;以确保系统是最新的&#xff1a; yum update2、使用以下命令安装JDK 1.8软件包&#xff1a; yum instal…

Unicode编码问题 如:\u529e\u7406\u9996\u6c7d\u52a0\u6cb9

python 遇到\u529e\u7406\u9996\u6c7d\u52a0\u6cb9解决方法&#xff1a; a "\u529e\u7406\u9996\u6c7d\u52a0"b a.decode("unicode-escape")print(b) 转载于:https://www.cnblogs.com/lilinpging/p/8440533.html

数组降维

写一个函数&#xff0c;打印数组内的内容&#xff0c;代码为&#xff1a; #include<stdio.h>void show_arr(int arr[], int num) {int i 0;for (i 0; i < num; i){printf("%d ", arr[i]);}printf("\n"); } int main() {int arr[] { 1,2,3,4,5…

洛谷P5638

不能排序。这里采用前缀和 #include<bits/stdc.h> using namespace std; int main(){long n,k;cin>>n>>k;vector<long long>arr(n-1);for(int i0;i<n-1;i){cin>>arr[i];if(i) arr[i]arr[i-1]; //计算前缀和}long long ansarr[n-2];if(k){for…

23、js - call()apply()bind()

<body><button>我是按钮</button> </body></html><script>// call apply bind 都可以改变this指向function fn(a, b) {const title ts;console.log(this);console.log(this.title);return a b;}const obj {title: vite}// 一、call()…

X11 转发

文章目录 参考参与者与凭据情景1 &#xff1a; 桌面终端下打开firefox情景2 &#xff1a; ssh 终端下打开firefox 情景1桌面终端下的 案例与DISPLAY 情景2基于X11转发的ssh案例ssh 终端下的 DISPLAY 情景2 下 vim 慢的问题 参考 什么是X11-forwarding&#xff1f;怎么使用&…