upload
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
#include "SessionManager.h"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include "SafeRNG.h"
|
||||
#ifndef _WIN32
|
||||
#include "UOHash.h"
|
||||
#else
|
||||
#include "../UOHash/UOHash.h"
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <json/json.h>
|
||||
|
||||
const std::chrono::hours SessionManager::Session::max_age = std::chrono::hours(24 * 5);
|
||||
|
||||
SessionManager::Session::Session() //默认构造函数:无效的已过期空Cookie
|
||||
{
|
||||
uid = -1;
|
||||
expiry_date = std::chrono::system_clock::now() - std::chrono::seconds(1);
|
||||
}
|
||||
|
||||
SessionManager::Session::Session(const Session& obj)
|
||||
{
|
||||
uid = obj.uid;
|
||||
cookie = obj.cookie;
|
||||
expiry_date = obj.expiry_date;
|
||||
}
|
||||
|
||||
SessionManager::Session::Session(const std::string& cookie)
|
||||
{
|
||||
uid = -1;
|
||||
this->cookie = cookie;
|
||||
expiry_date = std::chrono::system_clock::now() - std::chrono::seconds(1);
|
||||
}
|
||||
|
||||
SessionManager::Session::Session(const Json::Value& json_obj)
|
||||
{
|
||||
if(json_obj["UID"].isInt() && json_obj["Cookie"].isString() && (json_obj["ExpiryDate"].isInt64()))
|
||||
{
|
||||
uid = json_obj["UID"].asInt();
|
||||
cookie = json_obj["Cookie"].asString();
|
||||
auto ms = std::chrono::milliseconds(json_obj["ExpiryDate"].asInt64());
|
||||
expiry_date = systime(ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
uid = -1;
|
||||
expiry_date = std::chrono::system_clock::now() - std::chrono::seconds(1);
|
||||
}
|
||||
}
|
||||
|
||||
SessionManager::Session::Session(int uid, const std::string& cookie)
|
||||
{
|
||||
this->uid = uid;
|
||||
this->cookie = cookie;
|
||||
expiry_date = std::chrono::system_clock::now() + max_age;
|
||||
}
|
||||
|
||||
int SessionManager::Session::GetUID() const
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
bool SessionManager::Session::Expired() const
|
||||
{
|
||||
return (expiry_date < std::chrono::system_clock::now());
|
||||
}
|
||||
|
||||
std::string SessionManager::Session::GetCookie() const
|
||||
{
|
||||
return cookie;
|
||||
}
|
||||
|
||||
SessionManager::systime SessionManager::Session::GetExpiryDate() const
|
||||
{
|
||||
return expiry_date;
|
||||
}
|
||||
|
||||
bool SessionManager::Session::NeedExpiryDateRefresh() const
|
||||
{
|
||||
if (Expired())
|
||||
return true;
|
||||
auto usage_time = std::chrono::system_clock::now() - (expiry_date - max_age);
|
||||
return (usage_time >= std::chrono::hours(24));
|
||||
}
|
||||
|
||||
void SessionManager::Session::SetUID(int uid)
|
||||
{
|
||||
this->uid = uid;
|
||||
}
|
||||
|
||||
void SessionManager::Session::RefreshExpiryDate()
|
||||
{
|
||||
expiry_date = std::chrono::system_clock::now() + max_age;
|
||||
}
|
||||
|
||||
void SessionManager::Session::SetCookie(const std::string& cookie)
|
||||
{
|
||||
this->cookie = cookie;
|
||||
}
|
||||
|
||||
void SessionManager::Session::SetExpiryDate(const systime& expiry_time)
|
||||
{
|
||||
this->expiry_date = expiry_date;
|
||||
}
|
||||
|
||||
bool SessionManager::Session::operator<(const Session& obj) const
|
||||
{
|
||||
return (this->cookie < obj.cookie);
|
||||
}
|
||||
|
||||
SessionManager::Session::operator Json::Value() const
|
||||
{
|
||||
Json::Value root;
|
||||
root["UID"] = uid;
|
||||
root["Cookie"] = cookie;
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(expiry_date.time_since_epoch()).count();
|
||||
root["ExpiryDate"] = Json::Int64(ms);
|
||||
return root;
|
||||
}
|
||||
|
||||
int SessionManager::Session::GetMaxAgeSeconds()
|
||||
{
|
||||
return (max_age.count() * 3600);
|
||||
}
|
||||
|
||||
SessionManager::SessionManager()
|
||||
{
|
||||
}
|
||||
|
||||
std::string SessionManager::Random()
|
||||
{
|
||||
return RandomNumberGenerator::SecureRandomHex(32);
|
||||
}
|
||||
|
||||
std::string SessionManager::ISO8601_TimeString()
|
||||
{
|
||||
time_t now = time(nullptr);
|
||||
tm tim = {};
|
||||
#if defined(_WIN32)
|
||||
localtime_s(&tim, &now); // Windows
|
||||
#else
|
||||
localtime_r(&now, &tim); // POSIX
|
||||
#endif
|
||||
return fmt::format("{}-{}-{}T{}:{}:{}+08:00", (tim.tm_year + 1900), (tim.tm_mon + 1), tim.tm_mday, tim.tm_hour, tim.tm_min, tim.tm_sec);
|
||||
}
|
||||
|
||||
void SessionManager::AutoCleanCookiePool()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pool_mutex); //自动清理前需要上锁
|
||||
for (auto it = cookie_storage.begin(); it != cookie_storage.end(); ) //正向遍历,删除无效/过期Session
|
||||
{
|
||||
const std::string& cookie = it->first;
|
||||
int uid = it->second;
|
||||
|
||||
auto rit = reverse_cookie_storage.find(uid);
|
||||
if (rit == reverse_cookie_storage.end()) //有Cookie无Session,删掉
|
||||
{
|
||||
it = cookie_storage.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
Session index_session(cookie.substr(prefix.length()));
|
||||
auto& rcs = rit->second;
|
||||
auto sit = rcs.find(index_session);
|
||||
if ((sit == rcs.end()) || sit->Expired()) //无Session或Session已过期,删掉
|
||||
{
|
||||
if (sit != rcs.end())
|
||||
rcs.erase(sit);
|
||||
if (rcs.empty())
|
||||
reverse_cookie_storage.erase(rit);
|
||||
it = cookie_storage.erase(it);
|
||||
continue;
|
||||
}
|
||||
++it; //前置++性能开销较低
|
||||
}
|
||||
|
||||
for (auto rit = reverse_cookie_storage.begin(); rit != reverse_cookie_storage.end(); ) //反向遍历,删除异常Session
|
||||
{
|
||||
auto& rcs = rit->second;
|
||||
for (auto sit = rcs.begin(); sit != rcs.end(); )
|
||||
{
|
||||
std::string key = prefix + sit->GetCookie();
|
||||
if (cookie_storage.find(key) == cookie_storage.end()) //正向没有反向有,异常Session(无法被利用)
|
||||
{
|
||||
auto sit_next = std::next(sit);
|
||||
rcs.erase(sit);
|
||||
sit = sit_next;
|
||||
}
|
||||
else
|
||||
++sit;
|
||||
}
|
||||
if (rcs.empty())
|
||||
{
|
||||
auto rit_next = std::next(rit);
|
||||
reverse_cookie_storage.erase(rit);
|
||||
rit = rit_next;
|
||||
}
|
||||
else
|
||||
++rit;
|
||||
}
|
||||
}
|
||||
|
||||
std::string SessionManager::GenerateCookieForUser(int uid, bool cookie_only)
|
||||
{
|
||||
//生成Cookie和Session
|
||||
std::string str_uid = std::to_string(uid);
|
||||
std::string raw_cookie = str_uid + ISO8601_TimeString() + str_uid + Random() + str_uid;
|
||||
auto res = uns::UOHash::HashString(uns::HashID::SHA3_224, raw_cookie);
|
||||
if (!res)
|
||||
return "";
|
||||
std::string cookie = res.GetResult();
|
||||
Session session(uid, cookie);
|
||||
//存储到Cookie池 - 使用互斥体保证线程安全
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pool_mutex);
|
||||
cookie_storage.insert({ (prefix + cookie), uid });
|
||||
if (reverse_cookie_storage.find(uid) == reverse_cookie_storage.end())
|
||||
reverse_cookie_storage.insert({ uid, { session } });
|
||||
else
|
||||
reverse_cookie_storage.at(uid).insert(session);
|
||||
}
|
||||
//返回用于响应的Cookie串
|
||||
if(cookie_only)
|
||||
return (prefix + cookie);
|
||||
else
|
||||
return fmt::format(fmt::runtime(cookie_template), cookie, Session::GetMaxAgeSeconds());
|
||||
}
|
||||
|
||||
std::string SessionManager::DeleteCookie(const std::string& cookie)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pool_mutex);
|
||||
if (cookie_storage.find(cookie) != cookie_storage.end())
|
||||
{
|
||||
int uid = cookie_storage.at(cookie);
|
||||
cookie_storage.erase(cookie);
|
||||
if (reverse_cookie_storage.find(uid) != reverse_cookie_storage.end())
|
||||
{
|
||||
auto& rcs = reverse_cookie_storage.at(uid);
|
||||
Session index_session(cookie.substr(prefix.length()));
|
||||
rcs.erase(index_session);
|
||||
if (rcs.empty())
|
||||
reverse_cookie_storage.erase(uid);
|
||||
}
|
||||
}
|
||||
//返回用于响应的Cookie串(注销浏览器端的Cookie)
|
||||
return fmt::format(fmt::runtime(cookie_template.substr(prefix.length())), cookie, 0);
|
||||
}
|
||||
|
||||
bool SessionManager::CheckRequestCookie(uns::RequestPtr request, int& uid)
|
||||
{
|
||||
uid = -1;
|
||||
if (!request->HasHeader("Cookie")) //未找到Cookie
|
||||
return false;
|
||||
std::string cookie = request->GetHeader("Cookie");
|
||||
std::lock_guard<std::mutex> lock(pool_mutex); //使用互斥体保证Cookie池的线程安全
|
||||
auto cit = cookie_storage.find(cookie);
|
||||
if (cit == cookie_storage.end()) //不正确的Cookie
|
||||
return false;
|
||||
uid = cit->second;
|
||||
if (reverse_cookie_storage.find(uid) == reverse_cookie_storage.end())
|
||||
return false; //理论上不会出现有Cookie没有UID的情况,仅作兜底处理
|
||||
Session index_session(cookie.substr(prefix.length()));
|
||||
auto& rcs = reverse_cookie_storage.at(uid);
|
||||
auto it = rcs.find(index_session);
|
||||
if (it == rcs.end()) //无有效Session
|
||||
return false;
|
||||
if (it->Expired()) //Cookie过期,删除该Session及对应的cookie。
|
||||
{
|
||||
rcs.erase(it); //删除Session
|
||||
if (rcs.empty()) //如果一个用户没有任何有效的Cookie,删除该用户的记录
|
||||
reverse_cookie_storage.erase(uid);
|
||||
cookie_storage.erase(cookie); //删除Cookie
|
||||
return false;
|
||||
}
|
||||
if (it->NeedExpiryDateRefresh()) //根据需要决定是否刷新Cookie
|
||||
{
|
||||
auto session = rcs.extract(it);
|
||||
session.value().RefreshExpiryDate();
|
||||
rcs.insert(std::move(session));
|
||||
}
|
||||
return true; //到达此处意味着找到有效的Cookie,并已完成必要的更新工作
|
||||
}
|
||||
|
||||
std::string SessionManager::DeleteAllCookieForUser(int uid, const std::string & current_cookie)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pool_mutex);
|
||||
if (reverse_cookie_storage.find(uid) != reverse_cookie_storage.end())
|
||||
{
|
||||
auto sessions = reverse_cookie_storage.at(uid);
|
||||
reverse_cookie_storage.erase(uid);
|
||||
for(const auto& session : sessions)
|
||||
{
|
||||
if(cookie_storage.find(session.GetCookie()) != cookie_storage.end())
|
||||
cookie_storage.erase(session.GetCookie());
|
||||
}
|
||||
}
|
||||
//返回用于响应的Cookie串(注销浏览器端的Cookie)
|
||||
return fmt::format(fmt::runtime(cookie_template.substr(prefix.length())), current_cookie, 0);
|
||||
}
|
||||
|
||||
bool SessionManager::HasDumpedCookie(const std::string& path)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
fs::path fn = (fs::path(path) / "unsc_sessions.json");
|
||||
std::error_code ec;
|
||||
return fs::is_regular_file(fn, ec);
|
||||
}
|
||||
|
||||
bool SessionManager::LoadDumpedCookie(const std::string& path)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
fs::path fn = (fs::path(path) / "unsc_sessions.json");
|
||||
std::fstream fin(fn.string(), std::ios::in);
|
||||
if(!fin.is_open())
|
||||
return false;
|
||||
try
|
||||
{
|
||||
Json::Reader reader;
|
||||
Json::Value cookies;
|
||||
if(!reader.parse(fin, cookies, false))
|
||||
{
|
||||
fin.close();
|
||||
return false;
|
||||
}
|
||||
fin.close();
|
||||
if(!cookies.isArray())
|
||||
return false;
|
||||
std::lock_guard<std::mutex> lock(pool_mutex); //使用互斥体保证Cookie池的线程安全
|
||||
for(const auto& cookie : cookies)
|
||||
{
|
||||
Session session(cookie);
|
||||
if(session.Expired())
|
||||
continue;
|
||||
int uid = session.GetUID();
|
||||
cookie_storage.insert({ (prefix + session.GetCookie()), uid });
|
||||
if (reverse_cookie_storage.find(uid) == reverse_cookie_storage.end())
|
||||
reverse_cookie_storage.insert({ uid, { session } });
|
||||
else
|
||||
reverse_cookie_storage.at(uid).insert(session);
|
||||
}
|
||||
std::error_code ec;
|
||||
fs::remove(fn, ec);
|
||||
return true;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionManager::DumpAllValidCookies(const std::string& path)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
fs::path fn = (fs::path(path) / "unsc_sessions.json");
|
||||
std::fstream fout(fn.string(), std::ios::out | std::ios::trunc);
|
||||
if(!fout.is_open())
|
||||
return false;
|
||||
Json::Value cookies(Json::arrayValue);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pool_mutex); //使用互斥体保证Cookie池的线程安全
|
||||
for(const auto& [uid, sessions] : reverse_cookie_storage)
|
||||
for(const auto& session : sessions)
|
||||
cookies.append(session);
|
||||
}
|
||||
Json::FastWriter writer;
|
||||
fout << writer.write(cookies) << std::endl;
|
||||
fout.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
SessionManager GlobalSessionManager;
|
||||
Reference in New Issue
Block a user