【计网】从零开始学习http协议 ---深入理解cookie和session

news2024/11/25 2:55:18

在这里插入图片描述

我的天空里没有太阳,
总是黑夜,
但并不暗,
因为有东西代替了太阳。
--- 东野圭吾 ---

从零开始学习http协议

  • 1 理解cookie
    • 1.1 什么是cookie
    • 1.2 验证cookie
    • 1.3 cookie的属性
  • 2 理解session
    • 2.1 什么是session
    • 2.2 验证session

1 理解cookie

1.1 什么是cookie

HTTP Cookie(也称为 Web Cookie、 浏览器 Cookie 或简称 Cookie) 是服务器发送到用户浏览器并保存在浏览器上的一小块数据, 它会在浏览器之后向同一服务器再次发起请求时被携带并发送到服务器上。 通常, 它用于告知服务端两个请求是否来自同一浏览器, 如保持用户的登录状态、 记录用户偏好等

HTTP协议(超文本传输协议)被设计为无状态(Stateless)和无连接(Connectionless)的:

  • 无状态(Stateless):无状态意味着HTTP协议本身不会保存客户端和服务器之间的交互历史。每次客户端与服务器交互时,都是独立的,服务器不会记忆前一次请求的状态。以下是几个关键点:
    1. 每次请求都是独立的:客户端发送请求到服务器,服务器响应请求,然后连接关闭。每个请求都包含了处理该请求所需的所有信息。
    2. 不保存上下文信息:服务器不会保存请求之间的任何信息。例如,如果你在网站上进行了登录,HTTP协议本身不会记住你的登录状态,需要通过其他机制(如cookies、session等)在后续请求中传递登录信息。
    3. 优点:无状态的特性简化了HTTP协议的实现,因为服务器不需要存储和管理状态信息,可以快速处理大量请求。
    4. 缺点:对于需要连续多个步骤完成的事务,如购物车、用户会话等,无状态的特性使得这些功能的实现变得复杂。
  • 无连接(Connectionless):意味着每次HTTP请求/响应完成后,客户端和服务器之间的连接就会关闭。这里的“无连接”主要指的是在HTTP/1.0中。
    1. 每次请求/响应后关闭连接:在HTTP/1.0中,客户端发送请求后,服务器处理请求并发送响应,然后TCP连接就被关闭了。
    2. 每次交互都需要建立连接:对于每个新的HTTP请求,都需要重新建立TCP连接。
    3. HTTP/1.1的改进:虽然在HTTP/1.0中,协议是无连接的,但在HTTP/1.1中引入了“持久连接”(也称为keep-alive连接),这使得在同一个TCP连接上可以传输多个HTTP请求/响应。但这仍然不意味着HTTP/1.1是有状态的,它只是在连接层面减少了建立和关闭的开销。
    4. 优点:无连接的模型简化了服务器的实现,因为服务器不需要维护大量并发的连接。
    5. 缺点:频繁地打开和关闭连接会导致网络延迟,特别是在HTTP/1.0中,这对于性能是一个问题。

总结来说,HTTP协议的无状态性意味着它不保存交互历史,无连接性(在HTTP/1.0中)意味着每个请求/响应后都会关闭连接。这些设计决策使得HTTP协议简单、轻量,但同时也带来了一些复杂性,尤其是在需要维护状态和性能优化方面。

访问网站时,服务端和客户端不会记录访问的这个网站。每次都是一次重新的请求,这样一般是没有什么问题的。但是当访问一些需要账号的网站(B站 , 爱奇艺),如果每次都重新请求是不是就意味着每次都要进行一次登录,但实际上每次访问时,我们会发现只需要登录一次,再次访问时就不需要输入账号和密码了,并且可以识别是否是VIP!这就是cookie的作用,可以标识用户状态,对用户的登录状态进行保持,方便随时验证用户身份!

我们在我们的服务器中可以进行cookie的设置,在http应答中加入cookie的报头

resp.AddHeader("Set-Cookie", "username=zhansan");
resp.AddHeader("Set-Cookie", "passwd=123456");

这时我们就能够在我们的网站上看到我们设置的cookie!
在这里插入图片描述
cookie是储存在浏览器上的,当浏览器进行登录操作发送请求时,服务器会进行cookie的处理。然后通过应答返回给浏览器,下一次浏览器再次申请时,就可以带着这个cookie进行请求,服务器就可以直接识别账号等信息了!
在这里插入图片描述
对于cookie的储存有两种:

  1. 内存级:储存在内存中,一般是会话cookie,浏览器关闭就自动失效了
  2. 文件级:储存在文件中,持久性cookie,可以设置过期时间

每次浏览器发送的请求都带着cookie,发送给服务器,就能自动识别,不再需要重复进行登录等操作!

1.2 验证cookie

我们在添加报头时,可以加入set-cookie,把用户名进行设置!

//...
	resp.AddHeader("Content-Length", std::to_string(content.size()));
    resp.AddHeader(suffix, _mini_type[suffix]);
    resp.AddHeader("Set-Cookie", "username=zhansan");
//...

这样应答中就会返回给浏览器set-cookie,浏览器就会报存cookie!再次发送请求时,http请求中就会带有cookie 的报头!
在这里插入图片描述

1.3 cookie的属性

cookie的完整格式是很丰富的(服务器可以设置很多条cookie):

Set-Cookie: username=peter; expires=Thu, 18 Dec 2024 12:00:00UTC; path=/; domain=.example.com; secure; HttpOnly

其中包含很多信息:
当服务器发送一个Set-Cookie响应头时,它会在客户端的浏览器中创建一个cookie。这个cookie会在随后的请求中由浏览器自动发送回服务器。下面是Set-Cookie头中各个属性的解释:

  1. username=peter:这是cookie的名称和值,用于存储用户名。
  2. expires=Thu, 18 Dec 2024 12:00:00 UTC:这个属性指定了cookie的过期时间(UTC时间),即在这个时间点之后cookie将不再被浏览器发送。在这个例子中,cookie将在2024年12月18日UTC时间12:00过期。
  3. path=/:这个属性定义了cookie的路径。在这里,路径是根目录(/),意味着cookie将在这个域的所有路径下发送。
  4. domain=.example.com:这个属性指定了cookie有效的域。这里使用了一个点号(.)作为前缀,表示cookie对于example.com及其所有子域都有效。
  5. secure:这个属性没有值,它指示浏览器仅在HTTPS请求中发送cookie,而在HTTP请求中不发送。
  6. HttpOnly:这个属性没有值,它指示cookie不能通过客户端脚本(如JavaScript)访问,这有助于减少跨站脚本攻击(XSS)的风险。

我们可以在服务器中加入这些信息,来深入理解cookie!首先我们实现一下加入过期时间,注意这里是过期时间是要严格按照格式进行打印!

std::string GetMonth(int day)
    {
        std::vector<std::string> Month = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
        return Month[day];
    }
    std::string GetWeekday(int day)
    {
        std::vector<std::string> Weekday = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        return Weekday[day];
    }
    // 设置cookie属性
    std::string ExpireTimeUseRFC1123(int t) // 设置过期时间
    {
        // 获取当前过期时间戳
        time_t timeout = time(nullptr) + t;
        // 通过gmtime函数将时间戳转换成UTC时间
        struct tm *tmp = gmtime(&timeout);
        // 将时间写入到时间缓冲区
        char timebuffer[256];
        // expires=Thu, 18 Dec 2024 12:00:00 UTC
        snprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %04d %02d:%02d:%02d UTC",
                 GetWeekday(tmp->tm_wday).c_str(),
                 tmp->tm_mday,
                 GetMonth(tmp->tm_mon).c_str(),
                 tmp->tm_year + 1900,
                 tmp->tm_hour,
                 tmp->tm_min,
                 tmp->tm_sec);
        return timebuffer;
    }

这样我们在设置这样的cookie:

//...
 std::string cookie = "username=zhansan";
 cookie += "; expires=" + ExpireTimeUseRFC1123(60) ;//过期时间设置为一分钟后
 resp.AddHeader("Set-Cookie", cookie);
//...

这样就可以得到具有过期时间的cookie!
在这里插入图片描述
接下来我们在来实现一下cookie的路径,这个直接加入就可以:

   cookie += "; path=";//设置cookie路径
   cookie += "/a/b";

在这里插入图片描述
这就意味着只有在/a/b的路径下才会发送这个cookie!

cookie的属性是很丰富和健全的,但是只使用cookie是由风险的,只使用cookie并不是当前的主流形式,cookie+session是更加完善的方法!接下来我们来看cookie到底有什么风险以及如何理解session

2 理解session

2.1 什么是session

单独使用cookie时,http应答会传送回来用户的信息,储存在用户的内存或者文件中。如果cookie被他人盗取了,那么其他就能够通过这个cookie知道用户的个人信息,这样就造成了信息泄露!为了更好保证安全性产生了cookie+session的形式

  • 当用户首次访问网站时, 服务器会为用户创建一个唯一的 Session ID, 并通过Cookie 将其发送到客户端。
  • 客户端在之后的请求中会携带这个 Session ID, 服务器通过 Session ID 来识别用户, 从而获取用户的会话信息。
  • 服务器通常会将 Session 信息存储在内存、 数据库或缓存中。

与 Cookie 相似, 由于 Session ID 是在客户端和服务器之间传递的, 因此也存在被窃取的风险。但是一般虽然 Cookie 被盗取了, 但是用户只泄漏了一个 Session ID, 私密信息暂时没有被泄露的风险。Session ID 便于服务端进行客户端有效性的管理, 比如异地登录。可以通过 HTTPS 和设置合适的 Cookie 属性(如 HttpOnly 和 Secure) 来增强安全性。

  • 总的来说,具体的信息都是储存在服务器中的,返回给用户的只是一个 键值用来查找信息!

2.2 验证session

根据session的特性,我们需要设计出session结构体。这个结构体储存着真正的cookie信息:

// 用来进行测试说明
class Session
{
public:
    Session(const std::string &username, const std::string &status)
        : _username(username), _status(status)
    {
        _create_time = time(nullptr); // 获取时间戳就行了,后面实际需要,就转化就转换一下
    }
    ~Session()
    {
    }

public:
    std::string _username = "";
    std::string _status = "";
    uint64_t _create_time = 0;
    uint64_t _time_out = 0; // 60*5过期时间
    std::string vip = "";   // 是否为vip
    int active = 0;         //
    std::string pos = "";
    // 当然还可以再加任何其他信息,看你的需求
    //...
};

session里可以带有很多信息,根据具体业务需求添加即可!!!
我们描述好了session结构,接下来就来进行组织,这里可以使用map,unordered_map等映射来进行。SessionManager需要支持添加sessionid与session的映射。

using session_ptr = std::shared_ptr<Session>;

class SessionManager
{
public:
    SessionManager()
    {
        srand(time(nullptr) ^ getpid());
    }
    std::string AddSession(session_ptr s)
    {
        LOG(DEBUG, "AddSession\n");
        uint32_t randomid = rand(); // 随机数+时间戳,实际有形成sessionid的库,比如boost uuid库,或者其他第三方库等
        std::string sessionid = std::to_string(randomid);
        LOG(DEBUG, "randomid:%s ptr:%s %s\n", sessionid.c_str(), s->_username.c_str(), s->_status.c_str());

        _sessions.insert(std::make_pair(sessionid , s));
        LOG(DEBUG, "AddSession success:%s\n", _sessions[sessionid]);

        return sessionid;
    }
    session_ptr GetSession(const std::string sessionid)
    {
        if (_sessions.find(sessionid) == _sessions.end())
            return nullptr;
        return _sessions[sessionid];
    }
    ~SessionManager()
    {
    }

private:
    std::map<std::string, session_ptr> _sessions;
};

这样我们的session管理的基础就设计好了。我们可以在应答中加入我们的session:

  • 当用户进入登录页面时如果是第一次登录就建立session
  • 如果用户不是进入登录页面,那么就根据用户传输会来的cookie报头中的sessionid找到对应的session结构体,这样就得到了用户的信息!
//...
				static int number = 0;
                if (hreq.Url() == "/login.html") // 用/login path向指定浏览器写入sessionid,并在服务器维护对应的session对象
                {   
                    LOG(DEBUG , "进入session判断\n");
                    std::string sessionid = hreq.SessionId();
                    LOG(DEBUG , "获取到session :%s\n" , sessionid .c_str());
                    if (sessionid.empty()) // 说明历史没有登陆过
                    {
                        LOG(DEBUG , "说明历史没有登陆过\n");
                        std::string user = "user-" + std::to_string(number++);
                        session_ptr s = std::make_shared<Session>(user, "logined");
                        LOG(DEBUG , "session user:%s status:%s\n" , s->_username.c_str() ,s->_status.c_str());
                        std::string sessionid = _session_manager->AddSession(s);
                        LOG(DEBUG, "%s 被添加, sessionid是: %s\n", user.c_str(), sessionid.c_str());
                        resp.AddHeader("Set-Cookie", "sessionid=" + sessionid);
                    }
                }
                else
                {
                    // 当浏览器在本站点任何路径中活跃,都会自动提交sessionid, 我们就能知道谁活跃了.
                    std::string sessionid = hreq.SessionId();
                    if (!sessionid.empty())
                    {
                        session_ptr s = _session_manager->GetSession(sessionid);
                        // 这个地方有坑,一定要判断服务器端session对象是否存在,因为可能测试的时候
                        // 浏览器还有历史sessionid,但是服务器重启之后,session对象没有了.
                        if (s != nullptr)
                            LOG(DEBUG, "%s 正在活跃.\n", s->_username.c_str());
                        else
                            LOG(DEBUG, "cookie : %s 已经过期, 需要清理\n", sessionid.c_str());
                    }
                }

//...

记住了一定要将httpsever底层的sessionmanager初始化!

这样我们就能成功的通过sessionid获取信息了:
在这里插入图片描述
我们使用多个客户端来试试:
在这里插入图片描述
可以发现,我们的服务器可以根据cookie中的sessionid找到对应的session!非常nice!!!
OK!这样就是cookie+session的通信版本!

总结:
HTTP Cookie 和 Session 都是用于在 Web 应用中跟踪用户状态的机制。 Cookie 是存储在客户端的, 而 Session 是存储在服务器端的。 它们各有优缺点, 通常在实际应用中会结合使用, 以达到最佳的用户体验和安全性!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2205385.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

考研笔试/上机经典编程题集合(持续更新并完善解题思路)

目录 一、程序设计经典编程题(C语言实现)1.1 判断一个字符串是否由另一个字符串旋转得到1.2 字符串左旋1.3 求最大公约数以及最小公倍数1.4 二、力扣2.1 面试题 17.04. 消失的数字 三、牛客网3.1 OR62 倒置字符串3.2 BC84 计算y的值 一、程序设计经典编程题(C语言实现) 1.1 判…

Java生成csv格式文件样例_快速详细

Java高效Excel处理工具EasyExcel&#xff1a;解决大文件读写难题 EasyExcel是一个基于Java的高效、简洁的Excel处理工具&#xff0c;专为解决大文件内存溢出问题而设计。它能够在不牺牲性能的前提下快速完成Excel文件的读写操作。相比于传统的Apache POI等框架&#xff0c;Eas…

Unity修改鼠标图片【超简单】

1.向Unity导入需要修改的鼠标图片&#xff0c;在Unity内设置图片的Texture Type为Cursor。 2.编写代码 [SerializeField] Texture2D mouseTex;//放图片 void Start() {Cursor.SetCursor(mouseTex, Vector2.zero, CursorMode.Auto); }3.代码挂载在某物体&#xff08;或者随便哪…

net ASp.net Core 文件上传 IFormFile IFormFileCollection

IFormFile 接口是 ASP.NET Core 中处理上传文件的标准方式。当客户端通过表单上传文件时&#xff0c;ASP.NET Core 会自动将上传的文件封装成一个 IFormFile 对象&#xff0c;然后你可以在服务器端通过这个对象来访问和处理文件。 IFormFile接口的使用场景包括但不限于&…

java:找不到符号符号: 变量 log解决方案

最近有个项目频繁的遇到这个问题.经常启动不起来,但是提代码正常. 这个问题是由于lombok插件未加载导致的Slf4j的日志异常. 解决思路: 1.插件里面写在lombok再重新安装.无效 2.重新加载项目.无效 3.重新构建项目.无效 4.删除代码重下代码.无效 5.检查该配置是否打开,已打开 6.…

离散数学-逻辑与证明基础1.3(命题等价)

介绍 定义 1 一个复合命题&#xff0c;如果无论其中命题变量的真值如何&#xff0c;它总是为真&#xff0c;则称为重言式&#xff08;tautology&#xff09;。一个复合命题&#xff0c;如果它总是为假&#xff0c;则称为矛盾式&#xff08;contradiction&#xff09;。一个既不…

JDK11~JDK17值得关注的更新整理

新增特性&#xff1a; 1、instanceof的模式匹配 (JDK 16) 这个特性增强了instanceof运算符&#xff0c;允许在检查一个对象的类型时直接进行模式匹配&#xff0c;这使得代码更加简洁和易于理解。 通常我们使用instanceof时&#xff0c;一般发生在需要对一个变量的类型进行判…

QT C++ 软键盘/悬浮键盘/触摸屏键盘的制作

目录 1、前言 2、界面设计 3、英文、数字的输入 4、符号的输入 5、中文的输入 6、中文拼音库的选择 7、其他 8、结语 1、前言 使用QT C在带显示器的Linux系统 开发板上&#xff08;树莓派等&#xff09;编写操作UI界面时&#xff0c;很多时候都需要一个软键盘来输入文字…

施工进度可视化:提升项目管理透明度

图扑施工可视化通过直观的数据展示&#xff0c;使项目各方能够实时了解进度和资源分配&#xff0c;识别潜在问题&#xff0c;提高沟通效率&#xff0c;优化决策流程&#xff0c;确保项目按时、高效、安全地推进。

22个IT运维领域黄金证书 每一个都含金量极高!

**1、阿里云认证&#xff1a;**阿里云提供多个层次的认证&#xff0c;包括ACA&#xff08;助理工程师&#xff09;、ACP&#xff08;专业工程师&#xff09;和ACE&#xff08;专家工程师&#xff09;&#xff0c;涵盖了云计算、大数据、云安全、人工智能等多个领域。 **2、ITS…

【text2sql】基于上下文文学习的MCS-SQL框架在Spider和BIRD取得了新SOTA

论文标题 “MCS-SQL: Leveraging Multiple Prompts and Multiple-Choice Selection For Text-to-SQL Generation” &#xff0c;发表于2024.5的arXiv。 1.摘要解读 研究背景&#xff1a;大型语言模型&#xff08;LLMs&#xff09;在文本到SQL&#xff08;Text-to-SQL&#xf…

Python中的help()函数引发错误:追踪错误并提供解决方案

Python 中的 help() 函数通常用于交互式帮助&#xff0c;它可以显示关于模块、类、函数、方法、关键字等的文档说明。一般情况下&#xff0c;help() 函数不会引发错误&#xff0c;但如果你在使用时遇到问题&#xff0c;可能与以下几种常见情况有关。 1、问题背景 在使用 Pytho…

宝藏推荐:精选十款知识库搭建软件

当今这个信息爆炸的时代&#xff0c;高效地管理和利用知识成为了各行各业追求的目标。无论是企业内部的协作&#xff0c;还是对外提供的信息服务&#xff0c;一个强大的知识库都是不可或缺的。为了帮助大家更好地守护和利用知识宝藏&#xff0c;以下是精选的十款知识库搭建软件…

新版 Notepad++ 下载与安装教程

一、软件准备&#xff1a;麻烦点我 二、双击下载好的 notepad 软件进行安装&#xff0c;选择 “简体中文”。 三、默认 “下一步” 安装。 四、单击 “我接受” 按钮。 五、自定义安装位置&#xff0c;个人建议安装在 D 盘。 六、选择组件&#xff0c;默认 “下一步”。 七、勾…

FFmpeg的简单使用【Windows】

目录 一、视频生成图片 静态图片 转码过程 动态图片gif 二、图片生成视频 三、FFmpeg常用参数命令 3.1 主要参数 3.1.1、-i 3.1.2、-f 3.1.3、-ss 3.1.4、-t 3.2 音频参数 3.2.1、-aframes 3.2.2、 -b:a 3.2.3、-ar 3.2.4、-ac 3.2.5、-acodec 3.2.6、-an 3…

《大规模语言模型从理论到实践》第一轮学习--分布式训练

基础知识 5分钟看懂电脑硬件配置 - 知乎 (zhihu.com) 显存 定义&#xff1a;显存是显卡上的专用高速缓存&#xff0c;用于存储图形处理器&#xff08;GPU&#xff09;在处理图像和视频数据时所需的临时数据。 功能&#xff1a;显存的主要作用是提供GPU快速访问的数据存储&a…

从桌面运维转到网络安全后,我是怎样成为大厂高级网络安全工程师

我的第一份工作是桌面运维&#xff0c;我的上一份工作是大厂高级网络安全工程师。 很多人都不知道网络安全工程师是具体做什么的&#xff0c;今天就来给大家细细说下。 网络安全工程师是保护信息系统和网络免受破坏、攻击或非法访问的专业人员。他们的工作内容主要包括以下几个…

poi通过在word中写入了表格,通过libreoffice转换成PDF后,word中刚才画的表格宽度无限拉伸问题的解决。

一、复现: poi版本: <poi>3.17</poi><poi-ooxml>3.17</poi-ooxml><poi-ooxml-schemas>3.17</poi-ooxml-schemas><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><versio…

单例模式和读者写者问题

文章目录 10. 线程安全的单例模式10.1 什么是设计模式10.2 什么是单例模式10.3 单例模式的特点10.4 饿汉方式和懒汉方式10.5 单例模式的线程池 11. STL和智能指针的线程安全 问题11.1 STL中的容器是否是线程安全的?11.2 智能指针是否是线程安全的? 12. 其他常见的各种锁13. 读…

【Linux】来查看当前系统的架构

使用 uname 命令 uname -m 使用 arch 命令 arch 查看 /proc/cpuinfo 文件 查找 model name 或 Processor 字段。 cat /proc/cpuinfo 使用 lscpu 命令 lscpu