深入理解HTTP Cookie

目录
- ` HTTP Cookie`
- `定义`
- `工作原理`
- `分类`
- `安全性`
- `用途`
- `认识 cookie`
- `基本格式`
- `实验测试 cookie`
当我们登录了B站过后,为什么下次访问B站就不需要登陆了?
- 问题:B 站是如何认识我这个登录用户的?
- 问题:HTTP 是无状态,无连接的,怎么能够记住我?
HTTP Cookie
定义
HTTP Cookie
(也称为 Web Cookie、浏览器 Cookie 或简称 Cookie)是服务器发送到用户浏览器并保存在浏览器上的一小块数据
,它会在浏览器之后向同一服务器再次发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态、记录用户偏好等。
工作原理
- 当用户第一次访问网站时,服务器会在响应的 HTTP 头中设置
Set-Cookie
字段(如Set-Cookie : user = zhangsan),用于发送 Cookie 到用户的浏览器。 - 浏览器在接收到 Cookie 后,会将其保存在本地(通常是按照域名进行存储)。
- 在之后的请求中,浏览器会自动在 HTTP 请求头中携带 Cookie 字段,将之前保存的 Cookie 信息发送给服务器。
分类
-
会话 Cookie(Session Cookie):在浏览器关闭时失效。
-
持久 Cookie(Persistent Cookie):带有明确的过期日期或持续时间,可以跨多个浏览器会话存在。
-
如果 cookie 是一个持久性的 cookie,那么它其实就是浏览器相关的,特定目录下的一个文件。但直接查看这些文件可能会看到乱码或无法读取的内容,因为 cookie 文件通常以二进制或 sqlite 格式存储。一般我们查看,直接在浏览器对应的选项中直接查看即可。
-
类似于下面这种方式:

安全性
- 由于 Cookie 是存储在客户端的,因此存在被篡改或窃取的风险。
用途
用户认证和会话管理
(最重要)- 跟踪用户行为
- 缓存用户偏好等
- 比如在 chrome 浏览器下,可以直接访问:link

认识 cookie
- HTTP 存在一个报头选项:
Set-Cookie
, 可以用来进行给浏览器设置 Cookie值。 - 在
HTTP 响应
报头中添加,客户端(如浏览器)
获取并自行设置并保存Cookie。
服务器发送Cookie
:
- 当客户端(如浏览器)
首次
请求服务器资源时,服务器可能会在HTTP响应中
包含一个或多个Set-Cookie头部。这些Set-Cookie头部指示客户端存储特定的信息(即Cookie)。 - 每个Set-Cookie头部都包含了Cookie的名称、值以及可选的属性,如过期时间(Expires/Max-Age)、作用域(Path)、安全性要求(Secure)、跨站策略(SameSite)以及是否只能通过HTTP接口访问(HttpOnly)等。
客户端接收并保存Cookie
:
- 浏览器接收到包含Set-Cookie头部的HTTP响应后,会解析这些头部,并根据其中的指令将Cookie存储到本地。
- 存储的Cookie会包含名称、值以及所有相关的属性。
- 浏览器会根据Cookie的过期时间和其他属性来决定何时删除这些Cookie。
客户端发送Cookie
:
- 当浏览器再次向同一服务器(或符合Cookie作用域的其他服务器)发送请求时,它会自动检查是否有与该请求相关的Cookie。如果有,浏览器会将这些Cookie附加到HTTP请求的Cookie头部,并发送给服务器。服务器接收到请求后,可以从Cookie头部中读取这些Cookie,并根据需要处理它们。
基本格式
完整的 Set-Cookie 示例
时间格式必须遵守 RFC 1123 标准,具体格式样例:Tue, 01 Jan 2030 12:34:56 GMT 或者 UTC
(推荐)。
关于时间解释
- Tue: 星期二(星期几的缩写)
- , : 逗号分隔符
- 18: 日期(两位数表示)
- Thu: 月份的缩写
- 2024: 年份(四位数)
- 12:34:56: 时间(小时、分钟、秒)
- GMT: 格林威治标准时间(时区缩写)
GMT 和 UTC 都曾是或现在是国际上重要的时间标准,但由于地球自转的不规则性和原子钟的精确性,UTC 已经成为了全球性的标准时间,而 GMT 则更多被用作历史和地理上的参考。
关于其他可选属性的解释
expires=<date>
:设置 Cookie 的过期日期/时间。如果未指定此属性,则Cookie 默认为会话 Cookie
,即当浏览器关闭时过期。path=<some_path>
:限制 Cookie 发送到服务器的哪些路径。默认为设置它的路径。- domain=<domain_name>:指定哪些主机可以接受该 Cookie。默认为设置它的主机。
- secure:仅当使用 HTTPS 协议时才发送 Cookie。这有助于防止Cookie 在不安全的 HTTP 连接中被截获。
- HttpOnly:标记 Cookie 为 HttpOnly,意味着该 Cookie 不能被客户端脚本(如 JavaScript)访问。这有助于防止跨站脚本攻击(XSS)。
以下是对 Set-Cookie 头部字段的简洁介绍

注意事项
- 每个 Cookie 属性都以分号(;)和空格( )分隔。
- 名称和值之间使用等号(=)分隔。
- 如果 Cookie 的名称或值包含特殊字符(如空格、分号、逗号等),则需要进行 URL 编码。
Cookie 的生命周期
- 如果设置了 expires 属性,则 Cookie 将在指定的日期/时间后过期。
- 如果没有设置 expires 属性,则 Cookie 默认为会话 Cookie,即当浏览器关闭时过期。
安全性考虑
- 使用 secure 标志可以确保 Cookie 仅在 HTTPS 连接上发送,从而提高安全性。
- 使用 HttpOnly 标志可以防止客户端脚本(如 JavaScript)访问 Cookie,从而防止 XSS 攻击。
- 通过合理设置 Set-Cookie 的格式和属性,可以确保 Cookie 的安全性、有效性和可访问性,从而满足 Web 应用程序的需求。
实验测试 cookie
测试 cookie 的关键性完整代码全部附在最后。
测试 cookie 写入到浏览器
resp.AddHeader("Set-Cookie: username=zhangsan;"); //响应中添加一行报头即可

测试自动提交

测试写入过期时间
- 这里要由我们自己形成 UTC 统一标准时间:
//时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTCstd::string GetMonthName(int month) {std::vector<std::string> months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};return months[month];}std::string GetWeekDayName(int day){std::vector<std::string> weekdays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};return weekdays[day];}std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来UTC时间{time_t timeout = time(nullptr) + t;struct tm *tm = gmtime(&timeout); // 这里不能用localtime,因为localtime是默认带了时区的. gmtime获取的就是UTC统一时间char timebuffer[1024];//时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTCsnprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %d %02d:%02d:%02d UTC", GetWeekDayName(tm->tm_wday).c_str(),tm->tm_mday,GetMonthName(tm->tm_mon).c_str(),tm->tm_year+1900,tm->tm_hour,tm->tm_min,tm->tm_sec);return timebuffer;}
测试路径 path

提交到非/a/b 路径下
- 比如:http://8.137.19.140:8888/a/x
- 比如:http://8.137.19.140:8888/
- 比如:http://8.137.19.140:8888/x/y
单独使用 Cookie,有什么问题?
- 我们写入的是测试数据,如果写入的是用户的私密数据呢?比如,用户名密码,浏览痕迹等。
- 本质问题在于这些用户私密数据在浏览器(用户端)保存,非常容易被人盗取,更重要的是,除了被盗取,还有就是用户私密数据也就泄漏了。
Cookie测试代码
#pragma once#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <ctime>
#include "TcpServer.hpp"const std::string HttpSep = "\r\n";// 可以配置的
const std::string homepage = "index.html";
const std::string wwwroot = "./wwwroot";class HttpRequest
{
public:HttpRequest() : _req_blank(HttpSep), _path(wwwroot){ }bool GetLine(std::string &str, std::string *line){auto pos = str.find(HttpSep);if (pos == std::string::npos)return false;*line = str.substr(0, pos); // \r\nstr.erase(0, pos + HttpSep.size());return true;}bool Deserialize(std::string &request){std::string line;bool ok = GetLine(request, &line);if (!ok)return false;_req_line = line;while (true){bool ok = GetLine(request, &line);if (ok && line.empty()){_req_content = request;break;}else if (ok && !line.empty()){_req_header.push_back(line);}else{break;}}return true;}~HttpRequest(){}
private:// http报文自动std::string _req_line; // method url http_versionstd::vector<std::string> _req_header;std::string _req_blank;std::string _req_content;// 解析之后的内容std::string _method;std::string _url; // /dira/dirb/x.html /dira/dirb/XX?usrname=100&&password=1234 /dira/dirbstd::string _http_version;std::string _path; // "./wwwroot"std::string _suffix; // 请求资源的后缀
};const std::string BlankSep = " ";
const std::string LineSep = "\r\n";class HttpResponse
{
public:HttpResponse() : _http_version("HTTP/1.0"), _status_code(200), _status_code_desc("OK"), _resp_blank(LineSep){}void SetCode(int code){_status_code = code;}void SetDesc(const std::string &desc){_status_code_desc = desc;}void MakeStatusLine(){_status_line = _http_version + BlankSep + std::to_string(_status_code) + BlankSep + _status_code_desc + LineSep;}void AddHeader(const std::string &header){_resp_header.push_back(header+LineSep);}void AddContent(const std::string &content){_resp_content = content;}std::string Serialize(){MakeStatusLine();std::string response_str = _status_line;for (auto &header : _resp_header){response_str += header;}response_str += _resp_blank;response_str += _resp_content;return response_str;}~HttpResponse() {}
private:std::string _status_line;std::vector<std::string> _resp_header;std::string _resp_blank;std::string _resp_content; // body// httpversion StatusCode StatusCodeDescstd::string _http_version;int _status_code;std::string _status_code_desc;
};class Http
{
private:std::string GetMonthName(int month) {std::vector<std::string> months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};return months[month];}std::string GetWeekDayName(int day){std::vector<std::string> weekdays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};return weekdays[day];}std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来UTC时间{time_t timeout = time(nullptr) + t;struct tm *tm = gmtime(&timeout); // 这里不能用localtime,因为localtime是默认带了时区的. gmtime获取的就是UTC统一时间char timebuffer[1024];//时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTCsnprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %d %02d:%02d:%02d UTC", GetWeekDayName(tm->tm_wday).c_str(),tm->tm_mday,GetMonthName(tm->tm_mon).c_str(),tm->tm_year+1900,tm->tm_hour,tm->tm_min,tm->tm_sec);return timebuffer;}
public:Http(uint16_t port){_tsvr = std::make_unique<TcpServer>(port, std::bind(&Http::HandlerHttp, this, std::placeholders::_1));_tsvr->Init();}std::string ProveCookieWrite() // 证明cookie能被写入浏览器{return "Set-Cookie: username=zhangsan;";}// resp.AddHeader(ProveCookieWrite()); //测试cookie被写入与自动提交std::string ProveCookieTimeOut(){return "Set-Cookie: username=zhangsan; expires=" + ExpireTimeUseRfc1123(60) + ";"; // 让cookie 1min后过期}std::string ProvePath(){return "Set-Cookie: username=zhangsan; path=/a/b;";}std::string ProveOtherCookie(){return "Set-Cookie: passwd=1234567890; path=/a/b;";}std::string HandlerHttp(std::string request){HttpRequest req;req.Deserialize(request);req.DebugHttp();lg.LogMessage(Debug, "%s\n", ExpireTimeUseRfc1123(60).c_str());HttpResponse resp;resp.SetCode(200);resp.SetDesc("OK");resp.AddHeader("Content-Type: text/html");// resp.AddHeader(ProveCookieWrite()); //测试cookie被写入与自动提交// resp.AddHeader(ProveCookieTimeOut()); //测试过期时间的写入// resp.AddHeader(ProvePath()); // 测试路径resp.AddHeader(ProvePath());resp.AddHeader(ProveOtherCookie());resp.AddContent("<html><h1>helloworld</h1></html>");return resp.Serialize();}void Run(){_tsvr->Start();}~Http(){}
private:std::unique_ptr<TcpServer> _tsvr;
};
相关文章:

深入理解HTTP Cookie
🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊 目录 HTTP Cookie定义工作原理分类安全性用途 认识 cookie基本格式实验测试 cookie 当我们登录了B站过后,为什么下次访问B站就…...

Python多进程编程:使用`multiprocessing.Queue`进行进程间通信
Python多进程编程:使用multiprocessing.Queue进行进程间通信 1. 什么是multiprocessing.Queue?2. 为什么需要multiprocessing.Queue?3. 如何使用multiprocessing.Queue?3.1 基本用法3.2 队列的其他操作3.3 队列的阻塞与超时 4. 适…...

Docker 常见命令
命令库:docker ps | Docker Docs 安装docker apt install docker.io docker ps -a 作用:显示所有容器 docker logs -f frps 作用:持续输出容器名称为frps的日志信息(监控) docker restart frps 作用:重…...

Map 双列集合根接口 HashMap TreeMap
Map接口是一种双列集合,它的每一个元素都包含一个键对象Key和值Value 键和值直接存在一种对应关系 称为映射 从Map集中中访问元素, 只要指定了Key 就是找到对应的Value 常用方法 HashMap实现类无重复键无序 它是Map 接口的一个实现类,用于存储键值映射关系,并且HashMap 集合没…...

Pip源设置(清华源)相关总结
1、临时使用 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package 2、永久更改pip源 升级 pip 到最新的版本 (>10.0.0) 后进行配置: pip install pip -U pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 如…...

编程入门攻略
编程小白如何成为大神?大学新生的最佳入门攻略 编程已成为当代大学生的必备技能,但面对众多编程语言和学习资源,新生们常常感到迷茫。如何选择适合自己的编程语言?如何制定有效的学习计划?如何避免常见的学习陷阱&…...

C++核心编程和桌面应用开发 第十一天(静态转换 动态转换 常量转换 重新解释转换)
目录 1.静态类型转换 1.1语法 1.2用法 2.动态类型转换 2.1语法 2.2用法 3.常量类型转换 3.1语法 3.2用法 4.重新解释转换 4.1语法 1.静态类型转换 1.1语法 static_cast<目标转换类型>(待转换变量) 1.2用法 可用于基本数据类型之间的转换。比如int和char之…...

Ubuntu-Ubuntu22.04下Anacodna3的qmake和Qt的qmake冲突问题
Ubuntu22.04下Anacodna3的qmake和Qt的qmake冲突问题 一、问题描述二、原因分析三、解决办法 一、问题描述 Ubuntu22.04下Anacodna3的qmake和Qt的qmake冲突问题 zhyzhy-HP:~/Sources/mpv-examples/libmpv/qt$ make g -c -pipe -g -Wall -Wextra -D_REENTRANT -fPIC -DQT_WIDGET…...

mysql用户管理(user表列信息介绍,本质,管理操作),数据库的权限管理(权限列表,权限操作)
目录 用户管理 介绍 user表 介绍 列信息 Host User *_priv authentication_string 用户管理的本质 操作 创建用户 删除用户 修改用户信息 修改密码 自己修改 root用户修改指定用户的密码 数据库的权限 权限列表 给用户授权 查看权限 回收权限 刷新权限 …...

AI工具 | Notion全新AI集成:搜索、内容生成、数据分析与智能聊天功能发布
新的 Notion AI 集成了搜索、生成内容、分析数据和智能聊天等功能,所有操作都可以在 Notion 内完成。依托于 GPT-4 和 Claude 等先进的 AI 模型,用户可以与 AI 聊天并获取针对各种话题的答案。 随时使用 在 Notion 页面右下角找到 AI 图标,点…...

微知-如何查看PCIe设备插入在哪个插槽以及对应的busid?(biosdecode)
背景 以前对于PCIe设备插入到服务器上,有几个slot(slot就是服务器硬件上的插槽)以及哪些插入了设备可用ipmitool查看(具体参考兄弟篇:https://blog.csdn.net/essencelite/article/details/139051451,但是无法知道某个…...

数据结构 —— 树和二叉树简介
目录 0.前言 1.树的认识 什么是树 树的相关概念 树的表示 孩子兄弟表示法 2.二叉树的认识 什么是二叉树 特殊的二叉树 满二叉树 完全二叉树 二叉树的性质 性质一 性质二 性质三 二叉树的存储 顺序存储 链式存储 0.前言 笔者我之前讲解的数据结构都是线性…...

ubuntu安装boost
下载官方安装包官方,我使用的是boost_1_86_0.zip版本 1、解压安装包 2、进入boost_1_86_0 3、./bootstrap.sh --prefix/path/ 4、./b2 5、sudo ./b2 install 6、~/.bashrc配置环境...

【Spring AI】Java实现类似langchain的第三方函数调用_原理与详细示例
Spring AI 介绍 :简化Java AI开发的统一接口解决方案 在过去,使用Java开发AI应用时面临的主要困境是没有统一且标准的封装库,导致开发者需要针对不同的AI服务提供商分别学习和对接各自的API,这增加了开发难度与迁移成本。而Sprin…...

CIM系统:智慧城市的数字基石
计算机集成制造系统(CIM)是智慧城市建设中的关键技术,它通过集成多种信息技术,为城市提供一个全面的数字化镜像。CIM系统不仅涉及建筑信息模型(BIM)、地理信息系统(GIS)、物联网&…...

Android中Fragment的使用场景与生命周期
Android中Fragment的使用场景和生命周期 在Android应用开发中,Fragment是一个非常重要的概念,它允许开发者将Activity拆分成多个可重用的UI组件,从而提供灵活的UI设计,简化Activity的复杂性,并适应不同的屏幕尺寸和方…...

输入网址后,浏览器是如何高效渲染出网页的?
当你打开浏览器,输入一个网址并按下回车,接下来发生的一切仿佛都在瞬间完成——网页很快加载出来,内容、图片、动画一应俱全,像魔法一样。然而,这背后却是一个复杂而高效的协作过程,涉及到浏览器内核的多个组件共同工作,特别是渲染线程的协调作用。那么,浏览器究竟是如…...

springboot单文件,多文件下载方式
简单大文件下载: /*** 下载大文件* param path 路径* param fileName 文件名* return* throws IOException*/ public static ResponseEntity<InputStreamResource> downloadFile(String path, String fileName) throws IOException {Path filePath Paths.ge…...

JIT详解
文章目录 JIT为什么说 Java 语言“编译与解释并存”? JIT原理JVM 架构简览JIT 编译流程JIT 编译器的实现优化策略方法内联逃逸分析 JIT 在Java中,JIT(Just-In-Time)编译器是Java虚拟机(JVM)的一个重要组成…...

线下陪玩导游系统软件源码,家政预约服务源码(h5+小程序+app)
游戏陪玩系统源码陪玩小程序源码搭建基于PHP+MySQL陪玩系统app源码陪玩系统定制开发服务、成品陪玩系统源码 系统基于Nginx或者Apache PHP7.3 数据库mysql5.6 前端为uniapp-vue2.0 后端为thinkphp6 有域名授权加密,其他开源可二开 演示源码下载 开…...

模拟退火算法最常见知识点详解与原理简介控制策略
章节目录 模拟退火算法简介与原理 算法的基本流程与步骤 关键参数与控制策略 模拟退火算法的应用领域 如何学习模拟退火算法 资源简介与总结 一、模拟退火算法简介与原理 重点详细内容知识点总结 1. 模拟退火算法简介 模拟退火算法(Simulated Annealing, SA&#x…...

C语言高效内存管理:对齐、缓存与位域
C语言高效内存管理:对齐、缓存与位域 一、内存对齐 1. 内存对齐的概念 内存对齐(Memory Alignment)是指数据在内存中存储时,其起始地址遵循特定的规则,使得数据能够被高效地访问。CPU通常以固定的字节数(…...

ES操作指南
# Creating a text file with the described Elasticsearch operations. es_operations """ Elasticsearch 基本操作语法: 1. 索引文档 (Index Documents): 自动生成 ID: POST /index_name/_doc { "field1": "value1", "…...

【黑苹果】记录MacOS升级Sonoma的过程
【黑苹果】记录MacOS升级Sonoma的过程 一、硬件二、提前说明三、准备OC四、选择驱动五、选择ACPI六、下载内核扩展七、其他问题 一、硬件 设备是神舟zx6-ct5da 具体参照下图 二、提前说明 本机器已经安装过 macOS Monterey 12.6,这次是升级到 macOS Sonoma 14。 …...

向“新”发力,朝“质”攀峰 | 资福医疗携手大圣胃肠一体内窥镜系统亮相江苏省医学会第八次健康管理学学术会议
伴随“健康中国”战略的深入实施,为进一步加强健康管理学科内涵建设,提升健康管理服务能力,促进健康管理学科创新及多部门、多产业交叉融合,2024年10月12~14日“江苏省医学会第八次健康管理学学术会议”在南京顺利召开…...

springboot项目多个数据源配置 dblink
当项目中涉及到多个数据库连接的时候该如何处理? 在对应的配置文件,配置对应的数据库情况,不过我确实没咋测试对于事务的处理我可以后续在多做测试 配置文件中配置对应的数据源 然后再使用的时候使用这个 DS(“pd_ob”)注解。 然后又长知识…...

leetcode中哈希的python解法:Counter()介绍
Counter 是 Python 的 collections 模块中的一个类,用于统计可迭代对象中元素的出现次数。Counter 是一种专门为计数设计的哈希表(字典),它的键是元素,值是元素出现的次数。 Counter 的特点: 继承自 dict…...

VAS1800Q奇力科技线性芯片电荷泵热处理AEC-Q1000
VAS1800Q是一款专为汽车应用设计的高效恒流LED驱动器。它具备多个显著特点,不仅提升了LED驱动效率,还大大减少了热量的产生,使其在汽车照明领域中具有极高的应用价值。本文将详细介绍VAS1800Q的技术参数、功能及其在实际应用中的优势。 主要…...

Java 枚举的 valueOf() 方法与 Stream API 查找枚举对象
文章目录 一、枚举类型概述二、valueOf() 方法详解1. 什么是 valueOf() 方法?2. 使用示例 三、使用 Stream API 查找枚举对象1. 使用 Stream 查找枚举对象2. 使用 Stream 统计枚举对象 四、总结推荐阅读文章 在 Java 中,枚举(enum)…...

Git的认识及基本操作
目录 一:Git的基本认识 二:Git的安装 三:Git的基本操作 1.创建本地仓库 2.配置Git 3.⼯作区、暂存区、版本库 4. 修改文件 5.版本回退 6.撤销修改 7.删除文件 一:Git的基本认识 1.实例引入 在日常当中我们常常会遇到这样的事,就是在做实验报告或者课设…...