HTTP cookie 与 session
1.username指明用户,expires是该持久cookie的失效时间,path和domain作为限制,只有url请求的是该路径的文件或子目录、发送的是特定的域名,那浏览器进程才会在http报头的选项中加上cookie。如果设置了secure,那么只有https请求,才会加上cookie,http就不行了2.Cookie中每个属性都以分号(;)和空格( )分隔, 属性的名称和值之间使用等号(=
Cookie
Cookie是用来标识http请求对应的用户的
HTTP Cookie(也称为 Web Cookie、浏览器 Cookie 或简称 Cookie)是服务器发送到
浏览器并保存在浏览器上的一小块数据,它会在浏览器之后向同一服务器再次发
起请求时被携带并发送到服务器上。通常,它用于告知服务器HTTP请求来自哪个用户,具体作用有,保持用户的登录状态、记录用户的偏好习惯
工作原理
1.当用户第一次访问服务器时,服务器会在HTTP响应的响应头中设置 Set-Cookie 选项,用于设置 Cookie 在浏览器上,cookie的本质就是数据,其保存方式有两种,一种是保存在内存中,那这就是会话cookie,浏览器进程退出cookie就失效了,注意不是页面进程退出。还有一种是保存在文件中,那就是持久cookie,浏览器进程退出,该cookie也不会消失,持久cookie不是永久的,而是到了期限时间就失效。
2.在浏览器被设置cookie之后,浏览器发送的http请求中会携带 Cookie 选项,将之前保存的用户标识信息一起发送给服务器。
cookie分类
1.会话 Cookie(Session Cookie):保存在内存中,浏览器进程关闭时失效,注意是浏览器而不是页面。
2.持久 Cookie(Persistent Cookie):带有明确的过期日期或持续时间,保存在文件里, 浏览器退出了也不会失效。
cookie的设置
服务器在 HTTP 响应报文的响应头中添加 Set-Cookie选项,浏览器获取后设置Cookie,具体是保存在内存里还是文件里,看 Set-Cookie 中有没有 expires 字段,有那就是持久cookie,保存在文件中,没有就是会话cookie,保存在内存中
cookie实例:
1、会话级cookie的设置
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include <vector>
using namespace std;
char timebuffer[1024];
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 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;
}
class TcpServer
{
static void Service(int fd, struct sockaddr_in &peer)
{
cout << "client connected: " << inet_ntoa(peer.sin_addr) << " " << ntohs(peer.sin_port) << endl;
char buffer[1024];
while (1)
{
int n = read(fd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
cout << buffer;
const char *hello = "<h1>hello world</h1>";
string t= ExpireTimeUseRfc1123(60);
sprintf(buffer, "HTTP/1.0 200 OK\r\nContent-Length:%lu\r\nSet-Cookie: username=zhangsan; path=/a/b\r\n\r\n%s", strlen(hello), hello);
write(fd, buffer, strlen(buffer));
}
else if (n == 0)
{
cout << "连接断开,客户端为:" << inet_ntoa(peer.sin_addr) << " " << ntohs(peer.sin_port) << endl;
break;
}
}
}
struct ThreadData
{
int _fd;
struct sockaddr_in _peer;
ThreadData(int fd, struct sockaddr_in peer)
: _fd(fd), _peer(peer)
{
}
};
static void *ExecuteThread(void *arg)
{
pthread_detach(pthread_self()); // 分离线程,到时候线程函数执行完直接释放tcb
ThreadData *p = (ThreadData *)arg;
Service(p->_fd, p->_peer);
close(p->_fd);
delete p;
return nullptr; // BUG----如果线程启动函数执行完毕却没有返回值,那就会出现非法指令的情况
}
void ConnectionHandle(int fd, struct sockaddr_in &peer)
{
pthread_t t;
ThreadData *data = new ThreadData(fd, peer);
pthread_create(&t, nullptr, ExecuteThread, data);
}
public:
TcpServer(int port)
: _port(port)
{
}
void Init()
{
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(_port);
int opt = 1;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
listen(_sockfd, 8);
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(_sockfd, (struct sockaddr *)&peer, &len);
ConnectionHandle(fd, peer);
}
}
private:
int _port;
int _sockfd;
bool _isrunning;
};
int main()
{
TcpServer server(8888);
server.Init();
server.Start();
return 0;
}



使用相同的url访问两次服务器,第一次访问时,http请求中没有Cookie,然后浏览器收到http响应之后在内存中保存了Cookie信息,也就是会话级Cookie,第二次访问时,url请求的路径是/a/b/c,所以http请求中会携带Cookie,信息是username=张三,这样服务器就能识别http请求对应的用户
2、持久级cookie的设置
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include <vector>
using namespace std;
char timebuffer[1024];
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 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;
}
class TcpServer
{
static void Service(int fd, struct sockaddr_in &peer)
{
cout << "client connected: " << inet_ntoa(peer.sin_addr) << " " << ntohs(peer.sin_port) << endl;
char buffer[1024];
while (1)
{
int n = read(fd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
cout << buffer;
const char *hello = "<h1>hello world</h1>";
string t= ExpireTimeUseRfc1123(60);
sprintf(buffer, "HTTP/1.0 200 OK\r\nContent-Length:%lu\r\nSet-Cookie: username=zhangsan; path=/a/b; expires=%s\r\n\r\n%s", strlen(hello), timebuffer, hello);
write(fd, buffer, strlen(buffer));
}
else if (n == 0)
{
cout << "连接断开,客户端为:" << inet_ntoa(peer.sin_addr) << " " << ntohs(peer.sin_port) << endl;
break;
}
}
}
struct ThreadData
{
int _fd;
struct sockaddr_in _peer;
ThreadData(int fd, struct sockaddr_in peer)
: _fd(fd), _peer(peer)
{
}
};
static void *ExecuteThread(void *arg)
{
pthread_detach(pthread_self()); // 分离线程,到时候线程函数执行完直接释放tcb
ThreadData *p = (ThreadData *)arg;
Service(p->_fd, p->_peer);
close(p->_fd);
delete p;
return nullptr; // BUG----如果线程启动函数执行完毕却没有返回值,那就会出现非法指令的情况
}
void ConnectionHandle(int fd, struct sockaddr_in &peer)
{
pthread_t t;
ThreadData *data = new ThreadData(fd, peer);
pthread_create(&t, nullptr, ExecuteThread, data);
}
public:
TcpServer(int port)
: _port(port)
{
}
void Init()
{
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(_port);
int opt = 1;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
listen(_sockfd, 8);
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(_sockfd, (struct sockaddr *)&peer, &len);
ConnectionHandle(fd, peer);
}
}
private:
int _port;
int _sockfd;
bool _isrunning;
};
int main()
{
TcpServer server(8888);
server.Init();
server.Start();
return 0;
}



使用相同的url访问两次服务器,第一次访问时,http请求中没有Cookie,然后浏览器收到http响应之后在文件中保存了Cookie信息,也就是持久级Cookie,第二次访问时,url请求的路径是/a/b/c,所以http请求中会携带Cookie,信息是username=张三,这样服务器就能识别http请求对应的用户
Set-Cookie 选项和Cookie选项
Set-Cookie: username=peter; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/; domain=.example.com; secure; HttpOnly
Cookie: username=zhangsan
RFC 1123 互联网时间格式标准
示例:
Tue, 16 Jul 2024 08:30:45 GMT
格式详解
-
星期几:3字母英文缩写(Mon, Tue, Wed等)
-
日期:2位数字(前导零可选)
-
月份:3字母英文缩写(Jan, Feb, Mar等)
-
年份:4位数字
-
时间:24小时制,HH:MM:SS
-
时间标准:GMT (格林尼治标准时间)或UTC (协调世界时)
set-cookie可选属性介绍

1.username指明用户,expires是该持久cookie的失效时间,path和domain作为限制,只有url请求的是该路径的文件或子目录、发送的是特定的域名,那浏览器进程才会在http报头的选项中加上cookie。如果设置了secure,那么只有https请求,才会加上cookie,http就不行了
2.Cookie中每个属性都以分号(;)和空格( )分隔, 属性的名称和值之间使用等号(=)分隔。
注意:
1、http请求中的cookie选项不会把set-cookie中的所有东西都带上,只会带上username=xxxx
2、cookie看似选项一大堆,实则核心就一个username,而http请求带上它,服务器就能识别出HTTP请求对应的用户
3、一次Set-Cookie只能设置一个Cookie的有效字段,如果要设置多个字段,就要多次收到Set-Cookie才可以
sprintf(buffer, "HTTP/1.0 200 OK\r\nContent-Length:%lu\r\nSet-Cookie: paswwd=63541; username=aqc; path=/a/b;\r\n\r\n%s", strlen(hello), hello);

Set-Cookie中有paswwd和username两个标识,但最后只会保存一个,
sprintf(buffer, "HTTP/1.0 200 OK\r\nContent-Length:%lu\r\nSet-Cookie: username=aqc; path=/a/b;\r\n\r\n%s", strlen(hello), hello);
不退出浏览器进程,将paswwd的cookie保留下来,然后修改代码,使username成为唯一的标识,然后重新访问

这就是我想表达的了
Session
Session是用来解决Cookie的使用中,用username来标识用户的不足的。
Cookie存储数据在客户端,如果存储的是username这样的重要数据,第一点是很容易被猜到和试出,第二点是服务器即使察觉到username被盗取,因为username一般可能被服务器很多地方使用到,牵扯太大,不好修改,第三点username的Cookie设置既可以是会话级别的,也可以是持久级别的,如果持久级别的话安全问题风险会大大增加。鉴于Cookie直接保存username来标识用户有这三个很大的缺点,我们选择使用sessionid来代替username保存在Cookie中
工作原理
1、当用户打开浏览器首次访问服务器时,服务器会创建一个随机的 Session ID,并通过 Set-Cookie 将sessionid发送到客户端,注意这个Cookie设置是会话级的,当浏览器退出时sessionid就会失效,重新打开浏览器访问服务器时会得到一个和上次不同的seesionid,这两个sessionid都对应着同一份用户管理信息
2、和username相比,第一点sessionid对用户没有标识度,第二点sessionid和用户管理数据没什么大联系,可以轻易被失效或修改,第三点服务器对于sessionid只设置会话级别的Cookie,当浏览器退出时sessionid就会失效,鉴于这三点sessionid就解决了Cookie使用username的不足
安全性
由于 Session ID 是在cookie中的,因此session id也存在被窃取的风险, 但由于sessionid是会话级别cookie设置,所以即使被盗取了,如果之前的进程退出,该sessionid就会失效,盗取者也就无法使用。而且sessionid和username相比,被暴力猜出几乎不可能。最重要的是,如果真被盗取了而且前一个进程也没有退出,sessionid依然有效,服务器也可以通过检测登录ip发生变化来主动使sessionid失效,因为sessionid和用户管理数据关系不大,可以轻易被失效
session实例:
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include <vector>
using namespace std;
char timebuffer[1024];
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 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;
}
class TcpServer
{
static void Service(int fd, struct sockaddr_in &peer)
{
cout << "client connected: " << inet_ntoa(peer.sin_addr) << " " << ntohs(peer.sin_port) << endl;
char buffer[1024];
while (1)
{
int n = read(fd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
cout << buffer;
const char *hello = "<h1>hello world</h1>";
string t= ExpireTimeUseRfc1123(60);
sprintf(buffer, "HTTP/1.0 200 OK\r\nContent-Length:%lu\r\nSet-Cookie: sessionid=63541; path=/a/b;\r\n\r\n%s", strlen(hello), hello);
write(fd, buffer, strlen(buffer));
}
else if (n == 0)
{
cout << "连接断开,客户端为:" << inet_ntoa(peer.sin_addr) << " " << ntohs(peer.sin_port) << endl;
break;
}
}
}
struct ThreadData
{
int _fd;
struct sockaddr_in _peer;
ThreadData(int fd, struct sockaddr_in peer)
: _fd(fd), _peer(peer)
{
}
};
static void *ExecuteThread(void *arg)
{
pthread_detach(pthread_self()); // 分离线程,到时候线程函数执行完直接释放tcb
ThreadData *p = (ThreadData *)arg;
Service(p->_fd, p->_peer);
close(p->_fd);
delete p;
return nullptr; // BUG----如果线程启动函数执行完毕却没有返回值,那就会出现非法指令的情况
}
void ConnectionHandle(int fd, struct sockaddr_in &peer)
{
pthread_t t;
ThreadData *data = new ThreadData(fd, peer);
pthread_create(&t, nullptr, ExecuteThread, data);
}
public:
TcpServer(int port)
: _port(port)
{
}
void Init()
{
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(_port);
int opt = 1;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
listen(_sockfd, 8);
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int fd = accept(_sockfd, (struct sockaddr *)&peer, &len);
ConnectionHandle(fd, peer);
}
}
private:
int _port;
int _sockfd;
bool _isrunning;
};
int main()
{
TcpServer server(8888);
server.Init();
server.Start();
return 0;
}


所谓session,不就是在cookie中保存一个随机字符串,在服务器端保存着对应的用户数据,会话级别的cookie设置嘛
favicon.ico
favicon.ico 这一路上真是见了太多太多了

1.favicon.ico其实是表示一个网站图标文件,通常用来显示在浏览器的标签页上、地址栏旁边和收藏夹中。这个文件名 favicon 是 "favorite icon" 的缩写,而 .ico 是图标文件的后缀
2.浏览器会为了获取图标文件而专门构建对应的tcp连接和http的GET请求,路径就是/favicon.ico
在浏览器中输入url
在浏览器中输入url之后,浏览器进程会利用url中的分隔符将url进行分割得到url的各部分的内容,然后对内容中需要编码的字符进行编码,接着将域名发送给本地DNS服务器,DNS服务器依次对根服务器、顶级域服务器、二级域服务器发送查询报文并接收对应响应,最终得到目标域名的ip地址,然后DNS服务器将IP地址发送给浏览器进程,浏览器收到域名对应ip后开始组装http请求报文,路径和查询字符串放在请求行中,ip和端口放在请求头的Host选项中。接着浏览器进程创建一个TCP通信套接字,和服务器进行连接,然后发送url对应的http请求,收到对应的http响应后,接着浏览器创建另一个套接字发送/favicon.ico路径的GET请求报文
更多推荐

所有评论(0)