HTTPcookie與session實現

1.HTTP Cookie

定義

HTTP Cookie (也稱為 Web Cookie 、瀏覽器 Cookie 或簡稱 Cookie )是服務器發送到
用戶瀏覽器并保存在瀏覽器上的一小塊數據,它會在瀏覽器之后向同一服務器再次發
起請求時被攜帶并發送到服務器上。通常,它用于告知服務端兩個請求是否來自同一
瀏覽器,如保持用戶的登錄狀態、記錄用戶偏好等。
工作原理
用戶第一次訪問網站時,服務器會在響應的HTTP頭中設置Set-Cookie字段,發送Cookie到用戶的瀏覽器上,瀏覽器接收到cookie后,會保存到本地,之后的請求中,瀏覽器會自動在HTTP請求頭中攜帶Cookie字段,將之前保存的Cookie信息發送到服務器。
分類
會話Cookie(Session Cookie):在瀏覽器關閉時失效
持久Cookie(Persistent Cookie):帶有明確的過期日期或者持續時間,可以跨多個瀏覽器會話存在。
如果是一個持久的cookie,其實就是瀏覽器相關的,特定目錄下的一個文件,但是無法直接查看文件,因為cookie文件一般時以二進制或者sqlite格式存儲,查看就需要到瀏覽器對應的cookie中查看。

認識cookie

HTTP存在一個報頭選項:Set-cookie,可以用來進行給瀏覽器設置cookie值。

在HTTP響應頭中添加,客服端獲取并自行設置并保存。

完整的Set-Cookie實例

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 基于地球的自轉和公轉,而 UTC 基于原子鐘。
?
準確度:由于 UTC 基于原子鐘,它比基于地球自轉的 GMT 更加精確。
在實際使用中, GMT UTC 之間的差別通常很小,大多數情況下可以互換使用。但
在需要高精度時間計量的場合,如科學研究、網絡通信等, UTC 是更為準確的選擇。
關于其他可選屬性的解釋
expires=<date> [ 要驗證 ] :設置 Cookie 的過期日期 / 時間。如果未指定此屬
性,則 Cookie 默認為會話 Cookie ,即當瀏覽器關閉時過期。
path=<some_path> [ 要驗證 ] :限制 Cookie 發送到服務器的哪些路徑。默認
為設置它的路徑。
domain=<domain_name> [ 了解即可 ] :指定哪些主機可以接受該 Cookie 。默
認為設置它的主機。
secure [ 了解即可 ] :僅當使用 HTTPS 協議時才發送 Cookie 。這有助于防止
Cookie 在不安全的 HTTP 連接中被截獲。
HttpOnly [ 了解即可 ] :標記 Cookie HttpOnly ,意味著該 Cookie 不能被
客戶端腳本(如 JavaScript )訪問。這有助于防止跨站腳本攻擊( XSS )。

2.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機制,Web應用可能需要依賴其他方式(如Cookie)來跟蹤用戶狀態。Cookie存儲在客戶端,容易被惡意程序獲取和篡改,從而導致隱私泄露。此外,沒有Session的加密通信工具可能無法提供端到端加密或去中心化存儲,從而增加數據被竊取或監控的風險。

有session好處
  • 數據存儲位置:Session數據存儲在服務器端,而不是客戶端。即使客戶端的Session ID被竊取,攻擊者也無法直接獲取到存儲在服務器上的會話數據。

  • 安全性:服務器可以更好地控制Session的生命周期,例如定期更新Session ID、限制Session的使用范圍等,從而降低被攻擊的風險。

補充
favicon.ico是一個網站圖標,通常顯示瀏覽器的標簽頁上,地址欄旁邊或者收藏夾中。

3.cookie實現

HttpProtocol.hpp文件

第一個類是實現網絡請求的,GetLine函數用來截取一行,以HttpSep(\r\n)來作為分隔符,則0到pos位置就是一行的內容了,把讀取到的內容在刪除掉,讀取下一行內容。Deserialize函數是序列化,request是網絡請求,用getline函數獲取一行出來,第一行是請求行,單獨存儲起來,然后就是一直死循環讀取剩下的報頭數據,直到讀到了空行,如果是空行的話ok為true且empty也會true則就會把剩下的內容全都放到req_content里,這里就是文本內容了,也是要單獨存儲的,empty不為空就說明還是在讀取報頭信息,就把報頭信息放到vector里面存儲,這里是把鍵和值一起放到string里面的。

#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);str.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);}elsebreak;}return true;}void DebugHttp(){std::cout<<"_req_line:"<<_req_line<<std::endl;for(auto& line:_req_header){std::cout<<"--->"<<line<<std::endl;}}~HttpRequest(){}
private:std::string _req_line;std::vector<std::string> _req_header;std::string _req_blank;std::string _req_content;std::string _method;std::string _url;std::string _http_version;std::string _path;std::string _suffix;
};

?第二個代碼是網絡響應的,構造函數設置網絡版本和狀態碼和狀態信息。SetCode函數設置狀態碼的值,SetDesc函數設置狀態信息,MakeStatusLine函數構造狀態行,網絡版本+空格號+狀態碼+空格號+狀態信息+換行拼接在一起。AddHeader函數把報頭信息+換行符插入到vector中。AddContent函數設置文內容。Serialize函數先構造出狀態行,然后把報頭信息一一取出拼接在狀態行的后面,for結束后標識狀態行和報頭信息已經填好了,接下來就是空行和文本內容的填充,把填充完整的響應返回。

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;std::string _http_version;int _status_code;std::string _status_code_desc;};

這個類就是網絡處理部分。構造函數要接收一個端口號,make_unique構造一個TcpServer對象出來,參數是端口號和一個bind對象,再調用Init函數。ProveCookieWrite函數和ProveCookieTimeOut函數都是添加報頭信息,一個是用戶名一個是用戶名和過期時間(這里設置一分鐘),ProvePath函數添加了路徑,ProveOtherCookie添加了密碼。HandlerHttp函數就是處理部分了,創建請求對象,調用請求的反序列化方法,顯示報頭信息,創建響應對象,設置狀態碼,狀態信息,添加報頭內容,添加文本內容helloworld(前端格式寫的,最后網頁訪問會看到helloworld),最后返回完整的且序列化后的信息。

class Http
{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(){return "Set-Cookie: username=zhangsan;";}std::string ProveCookieTimeOut(){return "Set-Cookie: username=zhangsan;expires="+ExpireTimeUseRfc1123(60)+";";}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.SetCode(200);resp.SetDesc("OK");resp.AddHeader("Content-Type: text/html");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;
};

TcpServer.hpp文件

構造函數接收端口號和回調函數,以及套接字的值初始化等。ProcessConnection函數參數為Socket對象和InetAddr對象,調用sock方法recv在創建的套接字中讀取內容,調用handler處理讀取到的信息,把響應信息發送回去,關閉套接字(關閉后就不能傳入命令了 telnet指令)。Start函數則會調用AcceptConnection函數進行連接(Tcp協議需要雙方連接才能通信),創建task_t對象賦值為bind對象,bind是第一個是函數地址,但是類的話不是這樣,類函數需要&來獲取函數地址,后面的都是參數,而類函數還需要傳遞this指針,因為類函數隱式參數this。接著再創建實例并調用插入函數把task插入進去。

#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <functional>
#include <memory>
#include "ThreadPool.hpp"
#include "Log.hpp"
#include "Comm.hpp"
#include "Socket.hpp"
#include "InetAddr.hpp"using namespace Net_Work;
const static int default_backlog = 6;
using task_t = std::function<void()>;
using handler_t = std::function<std::string(std::string)>;class TcpServer
{
public:TcpServer(uint16_t port,handler_t handler):_port(port),_isrunning(false),_listensock(new TcpSocket()),_handler(handler){}void Init(){_listensock->BuildListenSocketMethod(_port,default_backlog);}void ProcessConnection(std::shared_ptr<Socket> sock,InetAddr addr){std::string request_str;if(sock->Recv(&request_str,4096)){std::string response=_handler(request_str);sock->Send(response);sock->CloseSocket();}}void Start(){_isrunning=true;while(_isrunning){std::string clientip;uint16_t clientport;std::shared_ptr<Socket> sock=_listensock->AcceptConnection(&clientip,&clientport);if(sock==nullptr){continue;}InetAddr addr(clientip,clientport);task_t task=std::bind(&TcpServer::ProcessConnection,this,sock,addr);ThreadPool<task_t>::GetInstance()->Push(task);}_isrunning=false;}~TcpServer(){}
private:uint16_t _port;std::unique_ptr<Socket> _listensock;bool _isrunning;handler_t _handler;
};

成員函數與非成員函數的區別

  • 非成員函數 :對于非成員函數(即全局函數或命名空間內的函數),函數名本身可以直接作為函數的指針。例如,對于一個非成員函數 void foo(),可以將 foo 作為函數指針使用。

  • 成員函數 :成員函數屬于類的一部分,它們的調用需要一個對象上下文(即通過對象來調用)。因此,要獲取成員函數的地址,必須使用 & 操作符。例如,對于類 A 中的成員函數 void bar(),要獲取其地址,必須寫成 &A::bar

Main.cc文件

#include "HttpProtocol.hpp"
#include <memory>using namespace std;void Usage(std::string proc)
{std::cout<<"Usage: \n\t"<<proc<<" local_port\n"<<std::endl;
}int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);return Usage_Err;}uint16_t port=stoi(argv[1]);std::unique_ptr<Http> http=make_unique<Http>(port);http->Run();return 0;
}

ThreadPool.hpp文件

#pragma once#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"static const int defaultnum=5;class ThreadData
{
public:ThreadData(const std::string& name):threadname(name){}~ThreadData(){}
public:std::string threadname;
};template<class T>
class ThreadPool
{
private:ThreadPool(int thread_num=defaultnum):_thread_num(thread_num){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_cond,nullptr);for(int i=0;i<_thread_num;i++){std::string threadname="thread-";threadname+=std::to_string(i+1);ThreadData td(threadname);_threads.emplace_back(threadname,std::bind(&ThreadPool<T>::ThreadRun,this,std::placeholders::_1),td);lg.LogMessage(Info,"%s is created...\n", threadname.c_str());}}ThreadPool(const ThreadPool<T>& tp)=delete;const ThreadPool<T>& operator=(const ThreadPool<T>)=delete;public:static ThreadPool<T>* GetInstance(){if(instance==nullptr){LockGuard lockguard(&sig_lock);if(instance==nullptr){lg.LogMessage(Info, "創建單例成功...\n");instance =new ThreadPool<T>();instance->Start();}}return instance;}bool Start(){for(auto& thread:_threads){thread.Start();lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());}return true;}void ThreadWait(const ThreadData& td){lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());pthread_cond_wait(&_cond,&_mutex);}void ThreadWakeup(){pthread_cond_signal(&_cond);}void checkSelf(){}void ThreadRun(ThreadData &td){while(true){T t;{LockGuard lockguard(&_mutex);while(_q.empty()){ThreadWait(td);lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());}t=_q.front();_q.pop();}t();}}void Push(T& in){LockGuard Lockguard(&_mutex);_q.push(in);ThreadWakeup();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}void Wait(){for(auto& thread: _threads){thread.Join();}}
private:std::queue<T> _q;std::vector<Thread<ThreadData>> _threads;int _thread_num;pthread_mutex_t _mutex;pthread_cond_t _cond;static ThreadPool<T>* instance;static pthread_mutex_t sig_lock;};
template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;

thread.hpp文件

#pragma once#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>// 設計方的視角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;template<class T>
class Thread
{
public:Thread(const std::string &threadname, func_t<T> func, T &data):_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data){}static void *ThreadRoutine(void *args) // 類內方法,{// (void)args; // 僅僅是為了防止編譯器有告警Thread *ts = static_cast<Thread *>(args);ts->_func(ts->_data);return nullptr;}bool Start(){int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);if(n == 0) {_isrunning = true;return true;}else return false;}bool Join(){if(!_isrunning) return true;int n = pthread_join(_tid, nullptr);if(n == 0){_isrunning = false;return true;}return false;}std::string ThreadName(){return _threadname;}bool IsRunning(){return _isrunning;}~Thread(){}
private:pthread_t _tid;std::string _threadname;bool _isrunning;func_t<T> _func;T _data;
};

代碼流程

main中unique_ptr創建了Http對象的智能指針,就會調用Http類的構造函數,會給Http的tsvr賦值上一個make出來的TcpServer對象,參數是端口號和bind對象,Tcp類也會調用構造函數確定了端口號和回調函數handler,接著調用init函數,里面會調用BuildListenSocketMethod創建套接字并初始化。這時走完了main的第20行代碼,接著21調用http的run函數,里面調用tsvr的Start函數,又到了Tcpserver部分,這里就會開始連接然后調用GetInstacnce函數,此時會跳到threadPool部分,執行threadpool的構造函數,鎖和條件變量初始化,然后開始往_threads里面插入線程名字和bind對象(ThreadRun函數),因為_thread<Thread<ThreadData>>,所以就會調用thread構造函數,確定了回調函數是Threadrun函數,然后再繼續執行GetInstance函數,開辟空間調用線程池的Start函數,start函數會調用所有線程的start函數(thread.start()),就會跳到thread部分,這里就會創建線程執行routine函數,然后調用回調函數threadrun,參數是線程對象ThreadData型,就會回到線程池部分,run會一直檢查任務隊列是否為空,為空就把這個線程放到條件變量中,不為空就取出任務隊列并執行,這里是全部到條件變量中等待,還沒有插入。此時才到Push(task)地方,開始插入任務,然后喚醒線程,喚醒的線程就會執行routine函數走回調函數func(Threadrun函數),run函數while隊列不是空,就可以取出任務執行了,而這個任務就是httphandler函數,處理請求,構造響應返回。

4.session實現

HttpProcotol.hpp文件

請求類方法實現,GetLine把套接字讀取的內容進行行劃分,以間隔號作為基準劃分多行。Parse函數把提取的行進行分離,stringstream字符串流,會以空格號為間隔把方法,uri和版本號提取出來存儲在成員變量中,接著定義prefix,然后范圍for遍歷報頭信息,如果報頭信息中有與prefix一樣的字符串就把跟prefix一樣的部分之后的地方提取出來存儲在cookie中,在插入到vector中管理,接著改變prefix值,在來一次,這次找的就是sessionid=,會提取=后面的值存儲到vector中管理。

#pragma once#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <ctime>
#include <functional>
#include "TcpServer.hpp"
#include "Session.hpp" // 引入sessionconst 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);str.erase(0,pos+HttpSep.size());return true;}void Parse(){std::stringstream ss(_req_line);ss>>_method>>_url>>_http_version;std::string prefix="Cookie:";for(auto& line:_req_header){std::string cookie;if(strncmp(line.c_str(),prefix.c_str(),prefix.size())==0){cookie=line.substr(prefix.size());_cookies.emplace_back(cookie);break;}}prefix="sessionid=";for(const auto& cookie:_cookies){if(strncmp(cookie.c_str(),prefix.c_str(),prefix.size())==0){_sessionid=cookie.substr(prefix.size());}}}std::string Url(){return _url;}std::string SessionId(){return _sessionid;}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; }void DebugHttp(){std::cout<<"_req_line:"<<_req_line<<std::endl;for(auto& line:_req_header){std::cout<<"--->"<<line<<std::endl;}}~HttpRequest(){}
private:std::string _req_line;std::vector<std::string> _req_header;std::string _req_blank;std::string _req_content;std::string _method;std::string _url;std::string _http_version;std::string _path;std::string _suffix;std::vector<std::string> _cookies;std::string _sessionid;
};

?處理請求的地方,構建請求和應答類,反序列化請求并提取請求行的信息,定義number變量,走if判斷,如果為login就說明要登陸,然后提取sessionid值,如果為空說明沒有登陸過,定義用戶名為number+1,創建session對象,會走session構造函數,得到名字和狀態,調用Sessionmanager類方法Addsession插入創建的s對象,會創建一個sessionid值出來,通過隨機數加時間戳,并把創建好的sessionid和s存儲到map里面管理,把sessionid信息添加到報頭中。如果不是login則會查看sessionid值,不為空就調用Getsession函數,在Session.hpp文件中的SessionManger類中成員變量_sessions中查看是否存在這個sessionid,存在就打印正在活躍,沒有就標識過期,后面就是設置狀態碼狀態信息等內容。

 std::string HandlerHttp(std::string request){HttpRequest req;HttpResponse resp;req.Deserialize(request);req.Parse();static int number=0;if(req.Url()=="/login"){std::string sessionid=req.SessionId();if(sessionid.empty()){std::string user="user-"+std::to_string(number++);session_ptr s=std::make_shared<Session>(user,"logined");std::string sessionid=_session_manager->AddSession(s);lg.LogMessage(Debug, "%s 被添加, sessionid是: %s\n", user.c_str(), sessionid.c_str());resp.AddHeader(ProveSession(sessionid));}}else{std::string sessionid=req.SessionId();if(!sessionid.empty()){session_ptr s=_session_manager->GetSession(sessionid);if(s!=nullptr)lg.LogMessage(Debug, "%s 正在活躍.\n", s->_username.c_str());elselg.LogMessage(Debug, "cookie : %s 已經過期, 需要清理\n", sessionid.c_str()); }}resp.SetCode(200);resp.SetDesc("OK");resp.AddHeader("Content-Type:text/html");resp.AddContent("<html><h1>helloworld</h1></html>");return resp.Serialize();}

Session.hpp文件

Session類主要是定義用戶名字和訪問什么文件,SessionManger則是管理session的類,添加session和檢驗并返回session。

#pragma once#include <iostream>
#include <string>
#include <memory>
#include <ctime>
#include <unistd.h>
#include <unordered_map>// 用來進行測試說明
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;uint64_t _time_out; // 60*5std::string vip; // vipint active; // std::string pos;//當然還可以再加任何其他信息,看你的需求
};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);_sessions.insert(std::make_pair(sessionid, s));return sessionid;}session_ptr GetSession(const std::string sessionid){if(_sessions.find(sessionid) == _sessions.end()) return nullptr;return _sessions[sessionid];}~SessionManager(){}
private:std::unordered_map<std::string, session_ptr> _sessions;
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/79323.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/79323.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/79323.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【算法基礎】冒泡排序算法 - JAVA

一、算法基礎 1.1 什么是冒泡排序 冒泡排序是一種簡單直觀的比較排序算法。它重復地走訪待排序的數列&#xff0c;依次比較相鄰兩個元素&#xff0c;如果順序錯誤就交換它們&#xff0c;直到沒有元素需要交換為止。 1.2 基本思想 比較相鄰元素&#xff1a;從頭開始&#xf…

0902Redux_狀態管理-react-仿低代碼平臺項目

文章目錄 1 Redux 概述1.1 核心概念1.2 基本組成1.3 工作流程1.4 中間件&#xff08;Middleware&#xff09;1.5 適用場景1.6 優缺點1.7 Redux Toolkit&#xff08;現代推薦&#xff09;1.8 與其他工具的對比1.9 總結 2 todoList 待辦事項案例3 Redux開發者工具3.1 核心功能3.2…

《ATPL地面培訓教材13:飛行原理》——第6章:阻力

翻譯&#xff1a;Leweslyh&#xff1b;工具&#xff1a;Cursor & Claude 3.7&#xff1b;過程稿 第6章&#xff1a;阻力 目錄 引言寄生阻力誘導阻力減少誘導阻力的方法升力對寄生阻力的影響飛機總阻力飛機總重量對總阻力的影響高度對總阻力的影響構型對總阻力的影響速度穩…

C++總結01-類型相關

一、數據存儲 1.程序數據段 ? 靜態&#xff08;全局&#xff09;數據區&#xff1a;全局變量、靜態變量 ? 堆內存&#xff1a;程序員手動分配、手動釋放 ? 棧內存&#xff1a;編譯器自動分配、自動釋放 ? 常量區&#xff1a;編譯時大小、值確定不可修改 2.程序代碼段 ?…

【Hot 100】94. 二叉樹的中序遍歷

目錄 引言二叉樹的中序遍歷我的解題代碼優化更清晰的表述建議&#xff1a; &#x1f64b;?♂? 作者&#xff1a;海碼007&#x1f4dc; 專欄&#xff1a;算法專欄&#x1f4a5; 標題&#xff1a;【Hot 100】94. 二叉樹的中序遍歷?? 寄語&#xff1a;書到用時方恨少&#xff…

大語言模型(LLMs)微調技術總結

文章目錄 全面總結當前大語言模型&#xff08;LLM&#xff09;微調技術1. 引言2. 為什么需要微調&#xff1f;3. 微調技術分類概覽4. 各種微調技術詳細介紹4.1 基礎微調方法4.1.1 有監督微調&#xff08;Supervised Fine-Tuning, SFT&#xff09;4.1.2 全參數微調&#xff08;F…

解決Maven項目中報錯“java不支持版本6即更高的版本 7”

錯誤背景 當Maven項目編譯或運行時出現錯誤提示 Java不支持版本6即更高的版本7&#xff0c;通常是由于項目配置的JDK版本與當前環境或編譯器設置不一致導致的。例如&#xff1a; 項目配置的Java版本為6或7&#xff0c;但實際使用的是JDK 17。Maven或IDE的編譯器未正確指定目標…

C++筆記-多態(包含虛函數,純虛函數和虛函數表等)

1.多態的概念 多態(polymorphism)的概念:通俗來說&#xff0c;就是多種形態。多態分為編譯時多態(靜態多態)和運行時多態(動態多態)&#xff0c;這里我們重點講運行時多態&#xff0c;編譯時多態(靜態多態)和運行時多態(動態多態)。編譯時多態(靜態多態)主要就是我們前面講的函…

【Unity】MVP框架的使用例子

在提到MVP之前&#xff0c;可以先看看這篇MVC的帖子&#xff1a; 【Unity】MVC的簡單分享以及一個在UI中使用的例子 MVC的不足之處&#xff1a; 在MVC的使用中&#xff0c;會發現View層直接調用了Model層的引用&#xff0c;即這兩個層之間存在著一定的耦合性&#xff0c;而MV…

前端js學算法-實踐

1、兩數之和 const twoSum (nums, target) > {const obj {}for (let m 0; m < nums.length; m) {const cur nums[m]const diff target - curif(obj.hasOwnProperty(diff)){ // 查詢對象中是否存在目標值-當前值鍵值對console.log([obj[diff], m]) // 存在則直接獲取…

《MATLAB實戰訓練營:從入門到工業級應用》趣味入門篇-用聲音合成玩音樂:MATLAB電子琴制作(超級趣味實踐版)

《MATLAB實戰訓練營&#xff1a;從入門到工業級應用》趣味入門篇-用聲音合成玩音樂&#xff1a;MATLAB電子琴制作&#xff08;超級趣味實踐版&#xff09; 開篇&#xff1a;當MATLAB遇見音樂 - 一場數字與藝術的浪漫邂逅 想象一下&#xff0c;你正坐在一臺古老的鋼琴前&#x…

實戰探討:為什么 Redis Zset 選擇跳表?

在了解了跳表的原理和實現后&#xff0c;一個常見的問題&#xff08;尤其是在面試中&#xff09;隨之而來&#xff1a;為什么像 Redis 的有序集合 (Zset) 這樣的高性能組件會選擇使用跳表&#xff0c;而不是大家熟知的平衡樹&#xff08;如紅黑樹&#xff09;呢&#xff1f; 對…

數據結構-線性結構(鏈表、棧、隊列)實現

公共頭文件common.h #define TRUE 1 #define FALSE 0// 定義節點數據類型 #define DATA_TYPE int單鏈表C語言實現 SingleList.h #pragma once#include "common.h"typedef struct Node {DATA_TYPE data;struct Node *next; } Node;Node *initList();void headInser…

高中數學聯賽模擬試題精選學數學系列第3套幾何題

△ A B C \triangle ABC △ABC 的內切圓 ⊙ I \odot I ⊙I 分別與邊 B C BC BC, C A CA CA, A B AB AB 相切于點 D D D, E E E, F F F, D D ′ DD DD′ 為 ⊙ I \odot I ⊙I 的直徑, 過圓心 I I I 作直線 A D ′ AD AD′ 的垂線 l l l, 直線 l l l 分別與 D E DE…

使用 ossutil 上傳文件到阿里云 OSS

在處理文件存儲和傳輸時&#xff0c;阿里云的對象存儲服務&#xff08;OSS&#xff09;是一個非常方便的選擇。特別是在需要批量上傳文件或通過命令行工具進行文件管理時&#xff0c;ossutil提供了強大的功能。本文將詳細說明如何使用 ossutil 上傳文件到阿里云 OSS&#xff0c…

DeepSeek與MySQL:開啟數據智能新時代

目錄 一、引言&#xff1a;技術融合的力量二、DeepSeek 與 MySQL&#xff1a;技術基石2.1 DeepSeek 技術探秘2.2 MySQL 數據庫深度解析 三、DeepSeek 與 MySQL 集成&#xff1a;從理論到實踐3.1 集成原理剖析3.2 集成步驟詳解 四、應用案例&#xff1a;實戰中的價值體現4.1 電商…

WebAPI項目從Newtonsoft.Json遷移到System.Text.Json踩坑備忘

1.控制器層方法返回類型不能為元組 控制器層方法返回類型為元組時&#xff0c;序列化結果為空。 因為元組沒有屬性只有field&#xff0c;除非使用IncludeFields參數專門指定&#xff0c;否則使用System.Text.Json進行序列化時不會序列化field var options new JsonSerializ…

202553-sql

目錄 一、196. 刪除重復的電子郵箱 - 力扣&#xff08;LeetCode&#xff09; 二、602. 好友申請 II &#xff1a;誰有最多的好友 - 力扣&#xff08;LeetCode&#xff09; 三、176. 第二高的薪水 - 力扣&#xff08;LeetCode&#xff09; 一、196. 刪除重復的電子郵箱 - 力扣…

Spring Boot的GraalVM支持:構建低資源消耗微服務

文章目錄 引言一、GraalVM原生鏡像技術概述二、Spring Boot 3.x的GraalVM支持三、適配GraalVM的關鍵技術點四、構建原生鏡像微服務實例五、性能優化與最佳實踐總結 引言 微服務架構已成為企業應用開發的主流模式&#xff0c;但隨著微服務數量的增加&#xff0c;資源消耗問題日…

pip 常用命令及配置

一、python -m pip install 和 pip install 的區別 在講解 pip 的命令之前&#xff0c;我們有必要了解一下 python -m pip install 和 pip install 的區別&#xff0c;以便于我們在不同的場景使用不同的方式。 python -m pip install 命令使用 python 可執行文件將 pip 模塊作…