🌈 个人主页:Zfox_
🔥 系列专栏:Linux
目录
- [前言:🔥 一种关于登录的场景演示 - B 站登录和未登录](#前言:🔥 一种关于登录的场景演示 - B 站登录和未登录)
- [一:🔥 引入 HTTP Cookie](#一:🔥 引入 HTTP Cookie)
-
- [🦋 定义](#🦋 定义)
- [🦋 工作原理](#🦋 工作原理)
- [🦋 分类](#🦋 分类)
- [🦋 安全性](#🦋 安全性)
- [🦋 用途](#🦋 用途)
- [二:🔥 认识 cookie](#二:🔥 认识 cookie)
-
- [🦋 基本格式](#🦋 基本格式)
- [🦋 完整的 Set-Cookie 示例](#🦋 完整的 Set-Cookie 示例)
- [🦋 注意事项](#🦋 注意事项)
- [🦋 Cookie 的生命周期](#🦋 Cookie 的生命周期)
- [🦋 单独使用 Cookie, 有什么问题?](#🦋 单独使用 Cookie, 有什么问题?)
- [三:🔥 引入 HTTP Session](#三:🔥 引入 HTTP Session)
-
- [🦋 定义](#🦋 定义)
- [🦋 工作原理](#🦋 工作原理)
- [🦋 安全性:](#🦋 安全性:)
- [🦋 超时和失效:](#🦋 超时和失效:)
- [🦋 用途:](#🦋 用途:)
- [🦋 模拟 session 行为](#🦋 模拟 session 行为)
- [🦋 实验测试 session:](#🦋 实验测试 session:)
- [四:🔥 共勉](#四:🔥 共勉)
前言:🔥 一种关于登录的场景演示 - B 站登录和未登录
🧑💻
- 问题: B 站是如何认识我这个登录用户的?
- 问题: HTTP 是无状态(并不知道你之前访问过哪个页面), 无连接的, 怎么能够记住我?
一:🔥 引入 HTTP Cookie
🦋 定义
🧑💻
HTTP Cookie
(也称为 Web Cookie、 浏览器 Cookie 或简称 Cookie) 是服务器发送到用户浏览器并保存在浏览器上的一小块数据, 它会在浏览器之后向同一服务器再次发起请求时被携带并发送到服务器上。 通常,它用于告知服务端两个请求是否来自同一浏览器, 如保持用户的登录状态、 记录用户偏好等。
🦋 工作原理
- 当用户第一次访问网站时, 服务器会在响应的 HTTP 头中设置 Set-Cookie字段, 用于发送 Cookie 到用户的浏览器。
- 浏览器在接收到 Cookie 后, 会将其保存在本地(通常是按照域名进行存储) 。
- 在之后的请求中, 浏览器会自动在 HTTP 请求头中携带 Cookie 字段, 将之前保存的 Cookie 信息发送给服务器。
🦋 分类
- 会话 Cookie( Session Cookie) : 在浏览器关闭时失效。
- 持久 Cookie( Persistent Cookie) : 带有明确的过期日期或持续时间,可以跨多个浏览器会话存在。
- 如果 cookie 是一个持久性的 cookie, 那么它其实就是浏览器相关的, 特定目录下的一个文件。 但直接查看这些文件可能会看到乱码或无法读取的内容,因为 cookie 文件通常以二进制或 sqlite 格式存储。 一般我们查看, 直接在浏览器对应的选项中直接查看即可。
📚 类似于下面这种方式:
🦋 安全性
- 📚 由于 Cookie 是存储在客户端的, 因此存在被篡改或窃取的风险
🦋 用途
- 用户认证和会话管理(最重要)
- 跟踪用户行为
- 缓存用户偏好等
- 比如在 chrome 浏览器下, 可以直接访问: chrome://settings/cookies
二:🔥 认识 cookie
- HTTP 存在一个报头选项: Set-Cookie, 可以用来进行给浏览器设置 Cookie值。
- 在 HTTP 响应头中添加, 客户端(如浏览器) 获取并自行设置并保存Cookie。
🦋 基本格式
cpp
Set-Cookie: <name>=<value>
其中 <name> 是 Cookie 的名称, <value> 是 Cookie 的值。
🦋 完整的 Set-Cookie 示例
cpp
C++
Set-Cookie: username=peter; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/; domain=.example.com; secure; HttpOnly
🧑💻 时间格式必须遵守 RFC 1123 标准, 具体格式样例: Tue, 01 Jan 2030 12:34:56 GMT 或者 UTC(推荐)。
🦁 关于时间解释
- Tue: 星期二(星期几的缩写)
- ,: 逗号分隔符
- 01: 日期(两位数表示)
- Jan: 一月(月份的缩写)
- 2030: 年份(四位数)
- 12:34:56: 时间(小时、 分钟、 秒)
- GMT: 格林威治标准时间(时区缩写)
🦁 GMT vs UTC --- 以下内容, 均来自文心一言,
了解即可
GMT
(格林威治标准时间) 和UTC
(协调世界时) 是两个不同的时间标准, 但它们在大多数情况下非常接近, 常常被混淆。 以下是两者的简单解释和区别:1. GMT(格林威治标准时间) :
GMT 是格林威治标准时间的缩写, 它是以英国伦敦的格林威治区为基准的世界时间标准。
GMT 不受夏令时或其他因素的影响, 通常用于航海、 航空、 科学、 天文等领域。
GMT 的计算方式是基于地球的自转和公转。
2. UTC(协调世界时) :UTC 全称为"协调世界时", 是国际电信联盟(ITU)制定和维护的标准时间。
UTC 的计算方式是基于原子钟, 而不是地球的自转, 因此它比 GMT 更准确。 据称, 世界上最精确的原子钟 50 亿年才会误差 1 秒。
UTC 是现在用的时间标准, 多数全球性的网络和软件系统将其作为标准时间。
📚 区别:
- 计算方式: GMT 基于地球的自转和公转, 而 UTC 基于原子钟。
- 准确度: 由于 UTC 基于原子钟, 它比基于地球自转的 GMT 更加精确。
🧑💻 在实际使用中, GMT 和 UTC 之间的差别通常很小, 大多数情况下可以互换使用。 但在需要高精度时间计量的场合, 如科学研究、 网络通信等, UTC 是更为准确的选择。
🧑💻 生成 UTC 代码如下:
cpp
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 ExpireTimeRfc1123(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, 2025, 12:00::00 UTC
snprintf(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;
}
📚 用法:
cpp
expires=" + ExpireTimeRfc1123(60000) + ";
🧑💻 关于其他可选属性的解释
expires=<date>[要验证]: 设置 Cookie 的过期日期/时间。 如果未指定此属性, 则 Cookie 默认为会话 Cookie, 即当浏览器关闭时过期。
path=<some_path>[要验证]: 限制 Cookie 发送到服务器的哪些路径。 默认为设置它的路径。
- domain=<domain_name> [了解即可]: 指定哪些主机可以接受该 Cookie。 默认为设置它的主机。
- secure [了解即可]: 仅当使用 HTTPS 协议时才发送 Cookie。 这有助于防止 Cookie 在不安全的 HTTP 连接中被截获。
- HttpOnly [了解即可]: 标记 Cookie 为 HttpOnly, 意味着该 Cookie 不能被客户端脚本(如 JavaScript) 访问。 这有助于防止跨站脚本攻击(XSS) 。
🦋 注意事项
- 每个 Cookie 属性都以分号(;) 和空格( ) 分隔。
- 名称和值之间使用等号(=) 分隔。
- 如果 Cookie 的名称或值包含特殊字符(如空格、 分号、 逗号等) , 则需要进行 URL 编码。
🦋 Cookie 的生命周期
- 如果设置了 expires 属性, 则 Cookie 将在指定的日期/时间后过期。
- 如果没有设置 expires 属性, 则 Cookie 默认为会话 Cookie, 即当浏览器关闭时过期。
🦋 单独使用 Cookie, 有什么问题?
- 我们写入的是测试数据, 如果写入的是用户的私密数据呢? 比如, 用户名密码,浏览痕迹等。
- 本质问题在于这些用户私密数据在浏览器(用户端)保存, 非常容易被人盗取, 更重要的是, 除了被盗取, 还有就是用户私密数据也就泄漏了。
三:🔥 引入 HTTP Session
🦋 定义
📚 HTTP Session 是服务器用来跟踪用户与服务器交互期间用户状态的机制。 由于 HTTP 协议是无状态的(每个请求都是独立的) , 因此服务器需要通过 Session 来记住用户的信息。
🦋 工作原理
- 当用户首次访问网站时, 服务器会为用户创建一个唯一的 Session ID, 并通过 Cookie 将其发送到客户端。
- 客户端在之后的请求中会携带这个 Session ID, 服务器通过 Session ID 来识别用户, 从而获取用户的会话信息。
- 服务器通常会将 Session 信息存储在内存、 数据库或缓存中。
🦋 安全性:
- 与 Cookie 相似, 由于 Session ID 是在客户端和服务器之间传递的, 因此也存在被窃取的风险。
- 但是一般虽然 Cookie 被盗取了, 但是用户只泄漏了一个 Session ID, 私密信息暂时没有被泄露的风险
- Session ID 便于服务端进行客户端有效性的管理, 比如异地登录。
- 可以通过 HTTPS 和设置合适的 Cookie 属性(如 HttpOnly 和 Secure) 来增强安全性
🦋 超时和失效:
- Session 可以设置超时时间, 当超过这个时间后, Session 会自动失效。
- 服务器也可以主动使 Session 失效, 例如当用户登出时。
🦋 用途:
- 用户认证和会话管理
- 存储用户的临时数据(如购物车内容)
- 实现分布式系统的会话共享(通过将会话数据存储在共享数据库或缓存中)
🦋 模拟 session 行为
Session.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <unordered_map>
#include <unistd.h>
class Session
{
public:
Session(const std::string &username, bool islogin)
:_username(username),
_islogin(islogin)
{
_create_time = time(nullptr);
}
~Session() {}
private:
std::string _username;
bool _islogin;
uint64_t _create_time;
// 其他信息:浏览痕迹
bool _isvip;
};
using session_ptr = std::shared_ptr<Session>;
class SessionManager
{
public:
SessionManager()
{
srand(time(nullptr) ^ getpid());
}
std::string AddSession(session_ptr s)
{
uint32_t randomid = rand() + time(nullptr); // 随机数 + 时间数,实际有形成sessionid的库,比如 boost uuid库,或者其他第三方库
std::string sessionid = std::to_string(randomid);
_session.insert(std::make_pair(sessionid, s));
return sessionid;
}
void DeleteSession(const std::string &sessionid)
{
auto iter = _session.find(sessionid);
if(iter != _session.end()) _session.erase(iter);
}
session_ptr GetSession(const std::string &sessionid)
{
auto iter = _session.find(sessionid);
if(iter != _session.end()) return _session[sessionid];
return nullptr;
}
~SessionManager() {}
private:
std::unordered_map<std::string, session_ptr> _session;
};
extern SessionManager _session_manager; // 声明
// session.cpp
SessionManager _session_manager; // 定义
🦋 实验测试 session:
cpp
void Login(HttpReauest &req, HttpResponse &resp)
{
// 根据req动态构建resp
LOG(LogLevel::DEBUG) << "进入登录模块" << req.Path() << ", " << req.Args();
std::string req_args = req.Args();
ssize_t pos = req_args.find("&");
std::string username = req_args.substr(0, pos);
std::string passwd = req_args.substr(pos + 1);
session_ptr s = std::make_shared<Session>(username, true);
std::string sessionid = _session_manager.AddSession(s);
LOG(LogLevel::DEBUG) << username << " 被添加,sessionid是:" << sessionid;
resp.SetHeader("Set-Cookie", "sessionid=" + sessionid + "; expires=" + ExpireTimeRfc1123(60000) + "; path=/;"); // 让 cookie 1min 过期
// 1. 解析参数格式,得到要的参数
// 2. 访问数据库,验证合法用, 其他工作...
// TODO
// 3. 登陆成功
std::string body = req.GetContent("wwwroot/login_success.html");
resp.SetCode(200);
resp.SetHeader("Content-Length", std::to_string(body.size()));
resp.SetHeader("Content-Type", "text/html");
resp.SetBody(body);
}
🧑💻 此时服务器端已经能识别是哪一个浏览器了
四:🔥 共勉
📚 HTTP Cookie
和 Session
都是用于在 Web 应用中跟踪用户状态的机制。 Cookie 是存储在客户端的, 而 Session 是存储在服务器端的
。 它们各有优缺点, 通常在实际应用中会结合使用, 以达到最佳的用户体验和安全性。
😋 想要完整代码可以私信博主噢!觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉