mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-06-16 21:15:57 +08:00
新增cookie登录鉴权模式,避免secret硬编码鉴权安全缺陷
This commit is contained in:
@@ -86,6 +86,7 @@ const string kSecret = API_FIELD"secret";
|
||||
const string kSnapRoot = API_FIELD"snapRoot";
|
||||
const string kDefaultSnap = API_FIELD"defaultSnap";
|
||||
const string kDownloadRoot = API_FIELD"downloadRoot";
|
||||
const string kLegacyAuth = API_FIELD"legacyAuth";
|
||||
|
||||
static onceToken token([]() {
|
||||
mINI::Instance()[kApiDebug] = "1";
|
||||
@@ -93,6 +94,7 @@ static onceToken token([]() {
|
||||
mINI::Instance()[kSnapRoot] = "./www/snap/";
|
||||
mINI::Instance()[kDefaultSnap] = "./www/logo.png";
|
||||
mINI::Instance()[kDownloadRoot] = "./www";
|
||||
mINI::Instance()[kLegacyAuth] = 1;
|
||||
});
|
||||
}//namespace API
|
||||
|
||||
@@ -101,18 +103,20 @@ using HttpApi = function<void(const Parser &parser, const HttpSession::HttpRespo
|
||||
// http api list
|
||||
static map<string, HttpApi, StrCaseCompare> s_map_api;
|
||||
|
||||
static void responseApi(const Json::Value &res, const HttpSession::HttpResponseInvoker &invoker){
|
||||
GET_CONFIG(string, charSet, Http::kCharSet);
|
||||
HttpSession::KeyValue headerOut;
|
||||
headerOut["Content-Type"] = string("application/json; charset=") + charSet;
|
||||
invoker(200, headerOut, res.toStyledString());
|
||||
};
|
||||
|
||||
static void responseApi(int code, const string &msg, const HttpSession::HttpResponseInvoker &invoker){
|
||||
static void responseApi(int code, const string &msg, const HttpSession::HttpResponseInvoker &invoker, ApiRetException *ex = nullptr){
|
||||
Json::Value res;
|
||||
HttpSession::KeyValue headerOut;
|
||||
if (ex) {
|
||||
res = ex->getBody();
|
||||
headerOut = ex->getHeaders();
|
||||
}
|
||||
res["code"] = code;
|
||||
res["msg"] = msg;
|
||||
responseApi(res, invoker);
|
||||
|
||||
GET_CONFIG(string, charSet, Http::kCharSet);
|
||||
headerOut["Content-Type"] = string("application/json; charset=") + charSet;
|
||||
|
||||
invoker(200, headerOut, res.toStyledString());
|
||||
}
|
||||
|
||||
static HttpApi toApi(const function<void(API_ARGS_MAP_ASYNC)> &cb) {
|
||||
@@ -304,12 +308,12 @@ static inline void addHttpListener(){
|
||||
try {
|
||||
it->second(parser, invoker, *helper);
|
||||
} catch (ApiRetException &ex) {
|
||||
responseApi(ex.code(), ex.what(), invoker);
|
||||
responseApi(ex.code(), ex.what(), invoker, &ex);
|
||||
helper->getPoller()->async([helper, ex]() { helper->shutdown(SockException(Err_shutdown, ex.what())); }, false);
|
||||
}
|
||||
#ifdef ENABLE_MYSQL
|
||||
catch (SqlException &ex) {
|
||||
responseApi(API::SqlFailed, StrPrinter << "操作数据库失败:" << ex.what() << ":" << ex.getSql(), invoker);
|
||||
responseApi(API::SqlFailed, StrPrinter << "操作数据库失败:" << ex.what() << ":" << ex.getSql(), invoker, &ex);
|
||||
}
|
||||
#endif // ENABLE_MYSQL
|
||||
catch (std::exception &ex) {
|
||||
@@ -363,7 +367,7 @@ Value ToJson(const PusherProxy::Ptr& p) {
|
||||
item["url"] = p->getUrl();
|
||||
item["status"] = p->getStatus();
|
||||
item["liveSecs"] = p->getLiveSecs();
|
||||
item["rePublishCount"] = p->getRePublishCount();
|
||||
item["rePublishCount"] = p->getRePublishCount();
|
||||
item["bytesSpeed"] = (Json::UInt64) p->getSendSpeed();
|
||||
item["totalBytes"] =(Json::UInt64) p->getSendTotalBytes();
|
||||
|
||||
@@ -708,6 +712,38 @@ void getThreadsLoad(TaskExecutorGetterImp &getter, API_ARGS_MAP_ASYNC) {
|
||||
});
|
||||
}
|
||||
|
||||
static constexpr char kLoginCookiePath[] = "/";
|
||||
static constexpr char kUnLoginCookieName[] = "ZLM_UNLOGIN";
|
||||
static constexpr char kLoginedCookieName[] = "ZLM_LOGINED";
|
||||
static constexpr size_t kUnLoginCookieLifeSeconds = 60;
|
||||
static constexpr size_t kLoginedCookieLifeSeconds = 24 * 3600;
|
||||
|
||||
void check_secret(toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsMap &allArgs, Json::Value &val) {
|
||||
GET_CONFIG(bool, legacy_auth , API::kLegacyAuth);
|
||||
GET_CONFIG(std::string, api_secret, API::kSecret);
|
||||
|
||||
auto ip = sender.get_peer_ip();
|
||||
if (!HttpFileManager::isIPAllowed(ip)) {
|
||||
throw AuthException("Your ip is not allowed to access the service.");
|
||||
}
|
||||
if (legacy_auth) {
|
||||
CHECK_ARGS("secret");
|
||||
if (api_secret != allArgs["secret"]) {
|
||||
throw AuthException("Incorrect secret");
|
||||
}
|
||||
} else {
|
||||
auto logined_cookie = HttpCookieManager::Instance().getCookie(kLoginedCookieName, allArgs.getParser().getHeader());
|
||||
if (!logined_cookie) {
|
||||
auto unlogin_cookie = HttpCookieManager::Instance().getCookie(kUnLoginCookieName, allArgs.getParser().getHeader());
|
||||
if (!unlogin_cookie) {
|
||||
unlogin_cookie = HttpCookieManager::Instance().addCookie(kUnLoginCookieName, "", kUnLoginCookieLifeSeconds);
|
||||
headerOut["Set-Cookie"] = unlogin_cookie->getCookie(kLoginCookiePath);
|
||||
}
|
||||
val["cookie"] = unlogin_cookie->getCookie();
|
||||
throw AuthException("Please login first", headerOut, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 安装api接口
|
||||
* 所有api都支持GET和POST两种方式
|
||||
@@ -720,7 +756,6 @@ void getThreadsLoad(TaskExecutorGetterImp &getter, API_ARGS_MAP_ASYNC) {
|
||||
*/
|
||||
void installWebApi() {
|
||||
addHttpListener();
|
||||
GET_CONFIG(string,api_secret,API::kSecret);
|
||||
|
||||
// 获取线程负载 [AUTO-TRANSLATED:3b0ece5c]
|
||||
// Get thread load
|
||||
@@ -2346,10 +2381,6 @@ void installWebApi() {
|
||||
|
||||
string subnet_prefix = allArgs["subnet_prefix"];
|
||||
|
||||
// if (subnet_prefix.empty()) {
|
||||
// subnet_prefix = "192.168.1"; //default ip prefix
|
||||
// }
|
||||
|
||||
auto result = std::make_shared<Value>(std::move(val));
|
||||
auto complete_token = std::make_shared<onceToken>(nullptr, [result, headerOut, invoker]() { invoker(200, headerOut, result->toStyledString()); });
|
||||
auto lam_search = [complete_token, result](const std::map<string, string> &device_info, const std::string &onvif_url) {
|
||||
@@ -2362,7 +2393,7 @@ void installWebApi() {
|
||||
//继续等待扫描
|
||||
return true;
|
||||
};
|
||||
OnvifSearcher::Instance().sendSearchBroadcast(move(subnet_prefix), std::move(lam_search), allArgs["timeout_ms"]);
|
||||
OnvifSearcher::Instance().sendSearchBroadcast(std::move(subnet_prefix), std::move(lam_search), allArgs["timeout_ms"]);
|
||||
});
|
||||
|
||||
api_regist("/index/api/getStreamUrl", [](API_ARGS_MAP_ASYNC) {
|
||||
@@ -2389,6 +2420,58 @@ void installWebApi() {
|
||||
});
|
||||
});
|
||||
|
||||
api_regist("/index/api/login", [](API_ARGS_MAP) {
|
||||
auto logined_cookie = HttpCookieManager::Instance().getCookie(kLoginedCookieName, allArgs.getParser().getHeader());
|
||||
if (logined_cookie) {
|
||||
// 已经登录成功
|
||||
val["code"] = API::Success;
|
||||
val["msg"] = "You are already logined";
|
||||
return;
|
||||
}
|
||||
CHECK_ARGS("digest");
|
||||
GET_CONFIG(std::string, api_secret, API::kSecret);
|
||||
|
||||
auto unlogin_cookie = HttpCookieManager::Instance().getCookie(kUnLoginCookieName, allArgs.getParser().getHeader());
|
||||
// MD5("zlmediakit:"+${secret}+":" +${cookie})
|
||||
auto digest_ok = unlogin_cookie ? MD5("zlmediakit:" + api_secret + ":" + unlogin_cookie->getCookie()).hexdigest() : "";
|
||||
if (!unlogin_cookie || digest_ok != allArgs["digest"]) {
|
||||
if (!unlogin_cookie) {
|
||||
unlogin_cookie = HttpCookieManager::Instance().addCookie(kUnLoginCookieName, "", kUnLoginCookieLifeSeconds);
|
||||
headerOut["Set-Cookie"] = unlogin_cookie->getCookie(kLoginCookiePath);
|
||||
}
|
||||
val["cookie"] = unlogin_cookie->getCookie();
|
||||
throw AuthException("Digest does not match, incorrect secret?", headerOut, val);
|
||||
}
|
||||
// 登录成功, cookie保持24小时
|
||||
logined_cookie = HttpCookieManager::Instance().addCookie(kLoginedCookieName, "", kLoginedCookieLifeSeconds);
|
||||
headerOut["Set-Cookie"] = logined_cookie->getCookie(kLoginCookiePath);
|
||||
|
||||
// 删除未登录状态的cookie
|
||||
unlogin_cookie->setExpired();
|
||||
HttpCookieManager::Instance().delCookie(unlogin_cookie);
|
||||
headerOut.emplace_force("Set-Cookie", unlogin_cookie->getCookie(kLoginCookiePath));
|
||||
|
||||
val["code"] = API::Success;
|
||||
});
|
||||
|
||||
api_regist("/index/api/logout", [](API_ARGS_MAP) {
|
||||
auto logined_cookie = HttpCookieManager::Instance().getCookie(kLoginedCookieName, allArgs.getParser().getHeader());
|
||||
if (logined_cookie) {
|
||||
// 已经登录成功, 删除cookie
|
||||
logined_cookie->setExpired();
|
||||
HttpCookieManager::Instance().delCookie(logined_cookie);
|
||||
headerOut["Set-Cookie"] = logined_cookie->getCookie(kLoginCookiePath);
|
||||
} else {
|
||||
val["msg"] = "You are not logined";
|
||||
}
|
||||
auto unlogin_cookie = HttpCookieManager::Instance().getCookie(kUnLoginCookieName, allArgs.getParser().getHeader());
|
||||
if (!unlogin_cookie) {
|
||||
unlogin_cookie = HttpCookieManager::Instance().addCookie(kUnLoginCookieName, "", kUnLoginCookieLifeSeconds);
|
||||
headerOut["Set-Cookie"] = unlogin_cookie->getCookie(kLoginCookiePath);
|
||||
}
|
||||
val["cookie"] = unlogin_cookie->getCookie();
|
||||
});
|
||||
|
||||
#if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
|
||||
VideoStackManager::Instance().loadBgImg("novideo.yuv");
|
||||
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastStreamNoneReader, [](BroadcastStreamNoneReaderArgs) {
|
||||
|
||||
Reference in New Issue
Block a user