Commit f3e551b3 by xiongziliang

支持可配置化的RTSP登录认证

parent 02aba328
...@@ -52,6 +52,9 @@ const char kBroadcastRtspSrcRegisted[] = "kBroadcastRtspSrcRegisted"; ...@@ -52,6 +52,9 @@ const char kBroadcastRtspSrcRegisted[] = "kBroadcastRtspSrcRegisted";
const char kBroadcastRtmpSrcRegisted[] = "kBroadcastRtmpSrcRegisted"; const char kBroadcastRtmpSrcRegisted[] = "kBroadcastRtmpSrcRegisted";
const char kBroadcastRecordMP4[] = "kBroadcastRecordMP4"; const char kBroadcastRecordMP4[] = "kBroadcastRecordMP4";
const char kBroadcastHttpRequest[] = "kBroadcastHttpRequest"; const char kBroadcastHttpRequest[] = "kBroadcastHttpRequest";
const char kBroadcastOnGetRtspRealm[] = "kBroadcastOnGetRtspRealm";
const char kBroadcastOnRtspAuth[] = "kBroadcastOnRtspAuth";
} //namespace Broadcast } //namespace Broadcast
//代理失败最大重试次数 //代理失败最大重试次数
...@@ -174,9 +177,13 @@ const char kPort[] = RTSP_FIELD"port"; ...@@ -174,9 +177,13 @@ const char kPort[] = RTSP_FIELD"port";
#define RTSP_SERVER_NAME "ZLServer" #define RTSP_SERVER_NAME "ZLServer"
const char kServerName[] = RTSP_FIELD"serverName"; const char kServerName[] = RTSP_FIELD"serverName";
const char kAuthBasic[] = RTSP_FIELD"authBasic";
onceToken token([](){ onceToken token([](){
mINI::Instance()[kPort] = RTSP_PORT; mINI::Instance()[kPort] = RTSP_PORT;
mINI::Instance()[kServerName] = RTSP_SERVER_NAME; mINI::Instance()[kServerName] = RTSP_SERVER_NAME;
//默认Md5方式认证
mINI::Instance()[kAuthBasic] = 0;
},nullptr); },nullptr);
} //namespace Rtsp } //namespace Rtsp
......
...@@ -72,6 +72,15 @@ extern const char kBroadcastRecordMP4[]; ...@@ -72,6 +72,15 @@ extern const char kBroadcastRecordMP4[];
extern const char kBroadcastHttpRequest[]; extern const char kBroadcastHttpRequest[];
#define BroadcastHttpRequestArgs const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed #define BroadcastHttpRequestArgs const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed
//该流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证
extern const char kBroadcastOnGetRtspRealm[];
#define BroadcastOnGetRtspRealmArgs const char *app,const char *stream,const RtspSession::onGetRealm &invoker
//请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败
//获取到密码后请调用invoker并输入对应类型的密码和密码类型,invoker执行时会匹配密码
extern const char kBroadcastOnRtspAuth[];
#define BroadcastOnRtspAuthArgs const char *user_name,bool must_no_encrypt,const RtspSession::onAuth &invoker
} //namespace Broadcast } //namespace Broadcast
//代理失败最大重试次数 //代理失败最大重试次数
...@@ -118,6 +127,8 @@ namespace Rtsp { ...@@ -118,6 +127,8 @@ namespace Rtsp {
extern const char kServerName[]; extern const char kServerName[];
extern const char kPort[]; extern const char kPort[];
//是否优先base64方式认证?默认Md5方式认证
extern const char kAuthBasic[];
} //namespace Rtsp } //namespace Rtsp
////////////RTMP服务器配置/////////// ////////////RTMP服务器配置///////////
......
...@@ -186,20 +186,21 @@ public: ...@@ -186,20 +186,21 @@ public:
return ret; return ret;
} }
static StrCaseMap parseArgs(const string &str){ static StrCaseMap parseArgs(const string &str,const char *pair_delim = "&", const char *key_delim = "="){
StrCaseMap ret; StrCaseMap ret;
auto arg_vec = split(str, "&"); auto arg_vec = split(str, pair_delim);
for (string &key_val : arg_vec) { for (string &key_val : arg_vec) {
if (!key_val.size()) { if (!key_val.size()) {
continue; continue;
} }
auto key_val_vec = split(key_val, "="); auto key_val_vec = split(key_val, key_delim);
if (key_val_vec.size() >= 2) { if (key_val_vec.size() >= 2) {
ret[key_val_vec[0]] = key_val_vec[1]; ret[key_val_vec[0]] = key_val_vec[1];
} }
} }
return ret; return ret;
} }
private: private:
string m_strMethod; string m_strMethod;
string m_strUrl; string m_strUrl;
......
...@@ -52,6 +52,11 @@ class RtspSession; ...@@ -52,6 +52,11 @@ class RtspSession;
class RtspSession: public TcpLimitedSession<MAX_TCP_SESSION> { class RtspSession: public TcpLimitedSession<MAX_TCP_SESSION> {
public: public:
typedef std::shared_ptr<RtspSession> Ptr; typedef std::shared_ptr<RtspSession> Ptr;
typedef std::function<void(const string &realm)> onGetRealm;
//encrypted为true是则表明是md5加密的密码,否则是明文密码
//在请求明文密码时如果提供md5密码者则会导致认证失败
typedef std::function<void(bool encrypted,const string &pwd_or_md5)> onAuth;
RtspSession(const std::shared_ptr<ThreadPool> &pTh, const Socket::Ptr &pSock); RtspSession(const std::shared_ptr<ThreadPool> &pTh, const Socket::Ptr &pSock);
virtual ~RtspSession(); virtual ~RtspSession();
void onRecv(const Socket::Buffer::Ptr &pBuf) override; void onRecv(const Socket::Buffer::Ptr &pBuf) override;
...@@ -83,6 +88,7 @@ private: ...@@ -83,6 +88,7 @@ private:
void inline send_UnsupportedTransport(); //不支持的传输模式 void inline send_UnsupportedTransport(); //不支持的传输模式
void inline send_SessionNotFound(); //会话id错误 void inline send_SessionNotFound(); //会话id错误
void inline send_NotAcceptable(); //rtsp同时播放数限制 void inline send_NotAcceptable(); //rtsp同时播放数限制
void splitRtspUrl(const string &url,string &app,string &stream);
inline bool findStream(); //根据rtsp url查找 MediaSource实例 inline bool findStream(); //根据rtsp url查找 MediaSource实例
inline void initSender(const std::shared_ptr<RtspSession> &pSession); //处理rtsp over http,quicktime使用的 inline void initSender(const std::shared_ptr<RtspSession> &pSession); //处理rtsp over http,quicktime使用的
...@@ -107,6 +113,13 @@ private: ...@@ -107,6 +113,13 @@ private:
inline void onRcvPeerUdpData(int iTrackIdx, const Socket::Buffer::Ptr &pBuf, const struct sockaddr &addr); inline void onRcvPeerUdpData(int iTrackIdx, const Socket::Buffer::Ptr &pBuf, const struct sockaddr &addr);
inline void startListenPeerUdpData(); inline void startListenPeerUdpData();
//认证相关
static void onAuthSuccess(const weak_ptr<RtspSession> &weakSelf);
static void onAuthFailed(const weak_ptr<RtspSession> &weakSelf,const string &realm);
static void onAuthUser(const weak_ptr<RtspSession> &weakSelf,const string &realm,const string &authorization);
static void onAuthBasic(const weak_ptr<RtspSession> &weakSelf,const string &realm,const string &strBase64);
static void onAuthDigest(const weak_ptr<RtspSession> &weakSelf,const string &realm,const string &strMd5);
char *m_pcBuf = nullptr; char *m_pcBuf = nullptr;
Ticker m_ticker; Ticker m_ticker;
Parser m_parser; //rtsp解析类 Parser m_parser; //rtsp解析类
...@@ -143,6 +156,9 @@ private: ...@@ -143,6 +156,9 @@ private:
bool m_bListenPeerUdpData = false; bool m_bListenPeerUdpData = false;
RtpBroadCaster::Ptr m_pBrdcaster; RtpBroadCaster::Ptr m_pBrdcaster;
//登录认证
string m_strNonce;
//RTSP over HTTP //RTSP over HTTP
function<void(void)> m_onDestory; function<void(void)> m_onDestory;
bool m_bBase64need = false; //是否需要base64解码 bool m_bBase64need = false; //是否需要base64解码
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "Rtmp/RtmpSession.h" #include "Rtmp/RtmpSession.h"
#include "Http/HttpSession.h" #include "Http/HttpSession.h"
#include "Shell/ShellSession.h" #include "Shell/ShellSession.h"
#include "Util/MD5.h"
#ifdef ENABLE_OPENSSL #ifdef ENABLE_OPENSSL
#include "Util/SSLBox.h" #include "Util/SSLBox.h"
...@@ -57,6 +58,57 @@ using namespace ZL::Shell; ...@@ -57,6 +58,57 @@ using namespace ZL::Shell;
using namespace ZL::Thread; using namespace ZL::Thread;
using namespace ZL::Network; using namespace ZL::Network;
#define REALM "realm_zlmedaikit"
static onceToken s_token([](){
NoticeCenter::Instance().addListener(nullptr,Config::Broadcast::kBroadcastOnGetRtspRealm,[](BroadcastOnGetRtspRealmArgs){
if(string("1") == stream ){
// live/1需要认证
EventPoller::Instance().async([invoker](){
//该流需要认证,并且设置realm
invoker(REALM);
});
}else{
//我们异步执行invoker。
//有时我们要查询redis或数据库来判断该流是否需要认证,通过invoker的方式可以做到完全异步
EventPoller::Instance().async([invoker](){
//该流我们不需要认证
invoker("");
});
}
});
NoticeCenter::Instance().addListener(nullptr,Config::Broadcast::kBroadcastOnRtspAuth,[](BroadcastOnRtspAuthArgs){
InfoL << "用户:" << user_name << (must_no_encrypt ? " Base64" : " MD5" )<< " 方式登录";
string user = user_name;
//假设我们异步读取数据库
EventPoller::Instance().async([must_no_encrypt,invoker,user](){
if(user == "test0"){
//假设数据库保存的是明文
invoker(false,"pwd0");
return;
}
if(user == "test1"){
//假设数据库保存的是密文
auto encrypted_pwd = MD5(user + ":" + REALM + ":" + "pwd1").hexdigest();
invoker(true,encrypted_pwd);
return;
}
if(user == "test2" && must_no_encrypt){
//假设登录的是test2,并且以base64方式登录,此时我们提供加密密码,那么会导致认证失败
//可以通过这个方式屏蔽base64这种不安全的加密方式
invoker(true,"pwd2");
return;
}
//其他用户密码跟用户名一致
invoker(false,user);
});
});
}, nullptr);
int main(int argc,char *argv[]){ int main(int argc,char *argv[]){
//设置退出信号处理函数 //设置退出信号处理函数
signal(SIGINT, [](int){EventPoller::Instance().shutdown();}); signal(SIGINT, [](int){EventPoller::Instance().shutdown();});
...@@ -68,8 +120,9 @@ int main(int argc,char *argv[]){ ...@@ -68,8 +120,9 @@ int main(int argc,char *argv[]){
//这里是拉流地址,支持rtmp/rtsp协议,负载必须是H264+AAC //这里是拉流地址,支持rtmp/rtsp协议,负载必须是H264+AAC
//如果是其他不识别的音视频将会被忽略(譬如说h264+adpcm转发后会去除音频) //如果是其他不识别的音视频将会被忽略(譬如说h264+adpcm转发后会去除音频)
auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks", auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks",
"rtmp://live.hkstv.hk.lxdns.com/live/hks"
//rtsp链接支持输入用户名密码 //rtsp链接支持输入用户名密码
"rtsp://admin:jzan123456@192.168.0.122/"}; /*"rtsp://admin:jzan123456@192.168.0.122/"*/};
map<string , PlayerProxy::Ptr> proxyMap; map<string , PlayerProxy::Ptr> proxyMap;
int i=0; int i=0;
for(auto &url : urlList){ for(auto &url : urlList){
...@@ -82,13 +135,14 @@ int main(int argc,char *argv[]){ ...@@ -82,13 +135,14 @@ int main(int argc,char *argv[]){
//http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4
//rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4
//rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4
PlayerProxy::Ptr player(new PlayerProxy("live",to_string(i++).data())); PlayerProxy::Ptr player(new PlayerProxy("live",to_string(i).data()));
//指定RTP over TCP(播放rtsp时有效) //指定RTP over TCP(播放rtsp时有效)
(*player)[RtspPlayer::kRtpType] = PlayerBase::RTP_TCP; (*player)[RtspPlayer::kRtpType] = PlayerBase::RTP_TCP;
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试 //开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试
player->play(url); player->play(url);
//需要保存PlayerProxy,否则作用域结束就会销毁该对象 //需要保存PlayerProxy,否则作用域结束就会销毁该对象
proxyMap.emplace(url,player); proxyMap.emplace(to_string(i),player);
++i;
} }
#ifdef ENABLE_OPENSSL #ifdef ENABLE_OPENSSL
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论