mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-06-13 11:41:44 +08:00
服务器cookie不再强制关联path
This commit is contained in:
285
src/Http/HttpCookieManager.cpp
Normal file
285
src/Http/HttpCookieManager.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "Util/util.h"
|
||||
#include "Util/MD5.h"
|
||||
#include "Common/config.h"
|
||||
#include "HttpCookieManager.h"
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
//////////////////////////////HttpServerCookie////////////////////////////////////
|
||||
HttpServerCookie::HttpServerCookie(const std::shared_ptr<HttpCookieManager> &manager,
|
||||
const string &cookie_name,
|
||||
const string &uid,
|
||||
const string &cookie,
|
||||
uint64_t max_elapsed){
|
||||
_uid = uid;
|
||||
_max_elapsed = max_elapsed;
|
||||
_cookie_uuid = cookie;
|
||||
_cookie_name = cookie_name;
|
||||
_manager = manager;
|
||||
manager->onAddCookie(_cookie_name,_uid,_cookie_uuid);
|
||||
}
|
||||
|
||||
HttpServerCookie::~HttpServerCookie() {
|
||||
auto strongManager = _manager.lock();
|
||||
if(strongManager){
|
||||
strongManager->onDelCookie(_cookie_name,_uid,_cookie_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
const string & HttpServerCookie::getUid() const{
|
||||
return _uid;
|
||||
}
|
||||
|
||||
string HttpServerCookie::getCookie(const string &path) const {
|
||||
return (StrPrinter << _cookie_name << "=" << _cookie_uuid << ";expires=" << cookieExpireTime() << ";path=" << path);
|
||||
}
|
||||
|
||||
const string& HttpServerCookie::getCookie() const {
|
||||
return _cookie_uuid;
|
||||
}
|
||||
|
||||
const string& HttpServerCookie::getCookieName() const{
|
||||
return _cookie_name;
|
||||
}
|
||||
|
||||
void HttpServerCookie::updateTime() {
|
||||
_ticker.resetTime();
|
||||
}
|
||||
|
||||
bool HttpServerCookie::isExpired() {
|
||||
return _ticker.elapsedTime() > _max_elapsed * 1000;
|
||||
}
|
||||
|
||||
string HttpServerCookie::cookieExpireTime() const{
|
||||
char buf[64];
|
||||
time_t tt = time(NULL) + _max_elapsed;
|
||||
strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
|
||||
return buf;
|
||||
}
|
||||
//////////////////////////////CookieManager////////////////////////////////////
|
||||
INSTANCE_IMP(HttpCookieManager);
|
||||
|
||||
HttpCookieManager::HttpCookieManager() {
|
||||
//定时删除过期的cookie,防止内存膨胀
|
||||
_timer = std::make_shared<Timer>(10,[this](){
|
||||
onManager();
|
||||
return true;
|
||||
}, nullptr);
|
||||
}
|
||||
|
||||
HttpCookieManager::~HttpCookieManager() {
|
||||
_timer.reset();
|
||||
}
|
||||
|
||||
void HttpCookieManager::onManager() {
|
||||
lock_guard<recursive_mutex> lck(_mtx_cookie);
|
||||
//先遍历所有类型
|
||||
for(auto it_name = _map_cookie.begin() ; it_name != _map_cookie.end() ;){
|
||||
//再遍历该类型下的所有cookie
|
||||
for (auto it_cookie = it_name->second.begin() ; it_cookie != it_name->second.end() ; ){
|
||||
if(it_cookie->second->isExpired()){
|
||||
//cookie过期,移除记录
|
||||
WarnL << it_cookie->second->getUid() << " cookie过期";
|
||||
it_cookie = it_name->second.erase(it_cookie);
|
||||
continue;
|
||||
}
|
||||
++it_cookie;
|
||||
}
|
||||
|
||||
if(it_name->second.empty()){
|
||||
//该类型下没有任何cooki记录,移除之
|
||||
WarnL << "该path下没有任何cooki记录:" << it_name->first;
|
||||
it_name = _map_cookie.erase(it_name);
|
||||
continue;
|
||||
}
|
||||
++it_name;
|
||||
}
|
||||
}
|
||||
|
||||
HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name,const string &uidIn,uint64_t max_elapsed,int max_client) {
|
||||
lock_guard<recursive_mutex> lck(_mtx_cookie);
|
||||
auto cookie = _geneator.obtain();
|
||||
auto uid = uidIn.empty() ? cookie : uidIn;
|
||||
auto oldCookie = getOldestCookie(cookie_name , uid, max_client);
|
||||
if(!oldCookie.empty()){
|
||||
//假如该账号已经登录了,那么删除老的cookie。
|
||||
//目的是实现单账号多地登录时挤占登录
|
||||
delCookie(cookie_name,oldCookie);
|
||||
}
|
||||
HttpServerCookie::Ptr data(new HttpServerCookie(shared_from_this(),cookie_name,uid,cookie,max_elapsed));
|
||||
//保存该账号下的新cookie
|
||||
_map_cookie[cookie_name][cookie] = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,const string &cookie) {
|
||||
lock_guard<recursive_mutex> lck(_mtx_cookie);
|
||||
auto it_name = _map_cookie.find(cookie_name);
|
||||
if(it_name == _map_cookie.end()){
|
||||
//不存在该类型的cookie
|
||||
return nullptr;
|
||||
}
|
||||
auto it_cookie = it_name->second.find(cookie);
|
||||
if(it_cookie == it_name->second.end()){
|
||||
//该类型下没有对应的cookie
|
||||
return nullptr;
|
||||
}
|
||||
if(it_cookie->second->isExpired()){
|
||||
//cookie过期
|
||||
it_name->second.erase(it_cookie);
|
||||
return nullptr;
|
||||
}
|
||||
return it_cookie->second;
|
||||
}
|
||||
|
||||
HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,const StrCaseMap &http_header) {
|
||||
auto it = http_header.find("Cookie");
|
||||
if (it == http_header.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto cookie = FindField(it->second.data(), (cookie_name + "=").data(), ";");
|
||||
if (!cookie.size()) {
|
||||
cookie = FindField(it->second.data(), (cookie_name + "=").data(), nullptr);
|
||||
}
|
||||
if(cookie.empty()){
|
||||
return nullptr;
|
||||
}
|
||||
return HttpCookieManager::Instance().getCookie(cookie_name , cookie);
|
||||
}
|
||||
|
||||
bool HttpCookieManager::delCookie(const HttpServerCookie::Ptr &cookie) {
|
||||
if(!cookie){
|
||||
return false;
|
||||
}
|
||||
return delCookie(cookie->getCookieName(),cookie->getCookie());
|
||||
}
|
||||
|
||||
bool HttpCookieManager::delCookie(const string &cookie_name,const string &cookie) {
|
||||
lock_guard<recursive_mutex> lck(_mtx_cookie);
|
||||
auto it_name = _map_cookie.find(cookie_name);
|
||||
if(it_name == _map_cookie.end()){
|
||||
return false;
|
||||
}
|
||||
return it_name->second.erase(cookie);
|
||||
}
|
||||
|
||||
void HttpCookieManager::onAddCookie(const string &cookie_name,const string &uid,const string &cookie){
|
||||
//添加新的cookie,我们记录下这个uid下有哪些cookie,目的是实现单账号多地登录时挤占登录
|
||||
lock_guard<recursive_mutex> lck(_mtx_cookie);
|
||||
//相同用户下可以存在多个cookie(意味多地登录),这些cookie根据登录时间的早晚依次排序
|
||||
_map_uid_to_cookie[cookie_name][uid][getCurrentMillisecond()] = cookie;
|
||||
}
|
||||
void HttpCookieManager::onDelCookie(const string &cookie_name,const string &uid,const string &cookie){
|
||||
lock_guard<recursive_mutex> lck(_mtx_cookie);
|
||||
//回收随机字符串
|
||||
_geneator.release(cookie);
|
||||
|
||||
auto it_name = _map_uid_to_cookie.find(cookie_name);
|
||||
if(it_name == _map_uid_to_cookie.end()){
|
||||
//该类型下未有任意用户登录
|
||||
return;
|
||||
}
|
||||
auto it_uid = it_name->second.find(uid);
|
||||
if(it_uid == it_name->second.end()){
|
||||
//该用户尚未登录
|
||||
return;
|
||||
}
|
||||
|
||||
//遍历同一名用户下的所有客户端,移除命中的客户端
|
||||
for(auto it_cookie = it_uid->second.begin() ; it_cookie != it_uid->second.end() ; ++it_cookie ){
|
||||
if(it_cookie->second != cookie) {
|
||||
//不是该cookie
|
||||
continue;
|
||||
}
|
||||
//移除该用户名下的某个cookie,这个设备cookie将失效
|
||||
it_uid->second.erase(it_cookie);
|
||||
|
||||
if(it_uid->second.size() != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
//该用户名下没有任何设备在线,移除之
|
||||
it_name->second.erase(it_uid);
|
||||
|
||||
if(it_name->second.size() != 0) {
|
||||
break;
|
||||
}
|
||||
//该类型下未有任何用户在线,移除之
|
||||
_map_uid_to_cookie.erase(it_name);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
string HttpCookieManager::getOldestCookie(const string &cookie_name,const string &uid, int max_client){
|
||||
lock_guard<recursive_mutex> lck(_mtx_cookie);
|
||||
auto it_name = _map_uid_to_cookie.find(cookie_name);
|
||||
if(it_name == _map_uid_to_cookie.end()){
|
||||
//不存在该类型的cookie
|
||||
return "";
|
||||
}
|
||||
auto it_uid = it_name->second.find(uid);
|
||||
if(it_uid == it_name->second.end()){
|
||||
//该用户从未登录过
|
||||
return "";
|
||||
}
|
||||
if(it_uid->second.size() < MAX(1,max_client)){
|
||||
//同一名用户下,客户端个数还没达到限制个数
|
||||
return "";
|
||||
}
|
||||
//客户端个数超过限制,移除最先登录的客户端
|
||||
return it_uid->second.begin()->second;
|
||||
}
|
||||
|
||||
/////////////////////////////////RandStrGeneator////////////////////////////////////
|
||||
string RandStrGeneator::obtain(){
|
||||
//获取唯一的防膨胀的随机字符串
|
||||
while (true){
|
||||
auto str = obtain_l();
|
||||
if(_obtained.find(str) == _obtained.end()){
|
||||
//没有重复
|
||||
_obtained.emplace(str);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
void RandStrGeneator::release(const string &str){
|
||||
//从防膨胀库中移除
|
||||
_obtained.erase(str);
|
||||
}
|
||||
|
||||
string RandStrGeneator::obtain_l(){
|
||||
//12个伪随机字节 + 4个递增的整形字节,然后md5即为随机字符串
|
||||
auto str = makeRandStr(12,false);
|
||||
str.append((char *)&_index, sizeof(_index));
|
||||
++_index;
|
||||
return MD5(str).hexdigest();
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
Reference in New Issue
Block a user