本文转自EasyDarwin开源团队成员的博客:http://blog.csdn.net/ss00_2012/article/details/52330838

在前面《EasyDarwin拉流支持基本认证和摘要认证》一文中讲述了如何通过修改qtaccess、qtusers来让EasyDarwin对我们创建的用户支持基本认证和摘要认证,之后在与群主的沟通中感觉这种方式的体验性太差,用户的需求是多方面的,可能有的想在配置文件中配置、有的想从数据库中读取、有的想在程序中写死……,我们需要提供一种便于用户自己扩展的方式,而不是与qtaccess、qtusers来打交道。

综上考虑,我们想实现在程序中指定用户名、密码的方式来作为用户后续扩展的一种呈现形式,这样无论用户是想从配置文件获取或者从数据库读取,代码的修改都比较简单。

下面的工作就是讲解如何修改EasyDarwin的代码来实现我们的目的。

使用工具:推流使用EasyPusher_File(下载地址为https://github.com/EasyDarwin/EasyPusher),拉流使用VLC,首先下载EasyDarwin工程代码(下载地址在https://github.com/EasyDarwin/EasyDarwin),当前文档使用的是2016.08.26下载的代码来进行操作的。然后按照文档进行编译运行,在调试之前我们先参考http://blog.csdn.net/cai6811376/article/details/52063666给QTSSAccessModule.cpp文件添加调试信息。之后我们尝试进行推流、拉流发现并没有“用户认证”提示,这是因为EasyDarwin默认的是跳过了对认证的处理,这段代码在RTSPSession::Run函数kRoutingRequest状态的处理中:

if (fRequest->SkipAuthorization())
{
// Skip the authentication and authorization states // The foll. normally gets executed at the end of the authorization state
// Prepare for kPreprocessingRequest state.
fState = kPreprocessingRequest; if (fRequest->GetMethod() == qtssSetupMethod)
// Make sure to erase the session ID stored in the request at this point.
// If we fail to do so, this same session would be used if another
// SETUP was issued on this same TCP connection.
fLastRTPSessionIDPtr.Len = 0;
else if (fLastRTPSessionIDPtr.Len == 0)
fLastRTPSessionIDPtr.Len = ::strlen(fLastRTPSessionIDPtr.Ptr); break;
}
else
fState = kAuthenticatingRequest;

可以看到fRequest->SkipAuthorization()的返回值决定了我们是否进行认证,在此我们将fRequest->SkipAuthorization()更改为if(false),然后在QTSSAccessModule.cpp中进行相应的更改:

  • 1.添加默认用户全局变量
static QTSS_AuthScheme sAuth = qtssAuthDigest;
static StrPtrLen sAuthRealm = "EasyDarwin";
static StrPtrLen sUserName = "admin";
static StrPtrLen sPassword = "admin";
static StrPtrLen sDigestPassword;

以上几个量分别表示用于认证的认证方法、认证领域、用户名、密码、摘要认证密码

  • 2.在Initialize()函数中添加以下语句来计算上面添加的摘要认证密码
StrPtrLen strptr;
CalcMD5HA1(&sUserName, &sAuthRealm, &sPassword, &strptr);
HashToString((unsigned char *)strptr.Ptr, &sDigestPassword);

这里面根据sAuthRealm、 sUserName、sPassword来计算sDigestPassword,使用了md5运算,需要进行头文件包含#include “md5digest.h”。

  • 3.将AuthenticateRTSPRequest()函数由之前的内容的更改为
QTSS_Error AuthenticateRTSPRequest(QTSS_RTSPAuth_Params* inParams)
{
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
UInt32 fileErr; OSMutexLocker locker(sUserMutex); if ((NULL == inParams) || (NULL == inParams->inRTSPRequest))
return QTSS_RequestFailed;
// Get the user profile object from the request object
QTSS_UserProfileObject theUserProfile = NULL;
UInt32 len = sizeof(QTSS_UserProfileObject);
QTSS_Error theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqUserProfile, 0, (void*)&theUserProfile, &len);
Assert(len == sizeof(QTSS_UserProfileObject));
if (theErr != QTSS_NoErr)
return theErr;
if (sAuth == qtssAuthNone)
{
// Get the authentication scheme from the request object
len = sizeof(sAuth);
theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&sAuth, &len);
Assert(len == sizeof(sAuth));
if (theErr != QTSS_NoErr)
return theErr;
}
else
{
theErr = QTSS_SetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&sAuth, sizeof(sAuth));
if (theErr != QTSS_NoErr)
return theErr;
} (void)QTSS_SetValue(theUserProfile, qtssUserRealm, 0, (void*)(sAuthRealm.Ptr), (sAuthRealm.Len)); // Get the username from the user profile object
char* usernameBuf = NULL;
theErr = QTSS_GetValueAsString(theUserProfile, qtssUserName, 0, &usernameBuf);
OSCharArrayDeleter usernameBufDeleter(usernameBuf);
StrPtrLen username(usernameBuf);
if (theErr != QTSS_NoErr)
return theErr;
// No memory is allocated; just a pointer to the profile is returned
if(!username.Equal(sUserName))//用户名不相等
{
return QTSS_NoErr;
}
if (sAuth == qtssAuthBasic)
(void)QTSS_SetValue(theUserProfile, qtssUserPassword, 0, (void*)(sPassword.Ptr), sPassword.Len);
else if (sAuth == qtssAuthDigest)
{
(void)QTSS_SetValue(theUserProfile, qtssUserPassword, 0, (void*)(sDigestPassword.Ptr), sDigestPassword.Len);
}
return QTSS_NoErr;
}

之前的代码有很多是读取qtaccess、qtusers的操作,比较繁琐,现在只用判断请求的用户名与我们设置的用户名是否一致,如果一致就将密码(基本认证)/摘要认证密码(摘要认证)写入到用户的theUserProfile对象中,之后在模块外进行密码比较的工作。

  • 4.将AccessAuthorizeRTSPRequest()函数代码更改为
QTSS_Error AccessAuthorizeRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
Bool16 allowNoAccessFiles = sAllowGuestDefaultEnabled; //no access files allowed means allowing guest access (unknown users)
QTSS_ActionFlags noAction = ~qtssActionFlagsRead; // allow any action
QTSS_ActionFlags authorizeAction = QTSSModuleUtils::GetRequestActions(inParams->inRTSPRequest); QTSS_Error theErr = QTSS_NoErr;
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest))
return QTSS_RequestFailed;
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
QTSS_UserProfileObject theUserProfile = QTSSModuleUtils::GetUserProfileObject(theRTSPRequest);
if (NULL == theUserProfile)
return QTSS_RequestFailed; char* username = QTSSModuleUtils::GetUserName_Copy(theUserProfile);
OSCharArrayDeleter usernameDeleter(username); Bool16 allowRequest;
if(username == NULL)
{
allowRequest = false;
}
else
{
allowRequest = (strcmp(username, sUserName.Ptr) == 0)?TRUE:FALSE;
} Bool16 founduser = allowRequest;
Bool16 authContinue = true;
QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, sAuthRealm.Ptr, ::strlen(sAuthRealm.Ptr)); if (allowRequest)
theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue);
if (!allowRequest)
theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue);
return theErr;
}

这个函数对功能是对读写权限进行判断,我们现在的更改比较简单,只要用户名密码正确,就拥有所有的读写权限。

好,经过了这些我们在再次运行程序,发现一推流就崩溃,最后定位到QTAccessFile.cpp->AuthorizeRequest->ReadEntireFile函数,这是因为之前在做SDP缓存的时候修改了QTSSModuleUtils::ReadEntireFile的代码,我们在QTSSModuleUtils.cpp中添加如下函数以及在QTSSModuleUtils.h中添加相应的函数声明。

QTSS_Error QTSSModuleUtils::ReadEntireFileEx(char* inPath, StrPtrLen* outData, QTSS_TimeVal inModDate, QTSS_TimeVal* outModDate)
{
QTSS_Object theFileObject = NULL;
QTSS_Error theErr = QTSS_NoErr; outData->Ptr = NULL;
outData->Len = 0;
do {
// Use the QTSS file system API to read the file
theErr = QTSS_OpenFileObject(inPath, 0, &theFileObject);
if (theErr != QTSS_NoErr)
break;
UInt32 theParamLen = 0;
QTSS_TimeVal* theModDate = NULL;
theErr = QTSS_GetValuePtr(theFileObject, qtssFlObjModDate, 0, (void**)&theModDate, &theParamLen);
Assert(theParamLen == sizeof(QTSS_TimeVal));
if(theParamLen != sizeof(QTSS_TimeVal))
break;
if(outModDate != NULL)
*outModDate = (QTSS_TimeVal)*theModDate; if(inModDate != -1) {
// If file hasn't been modified since inModDate, don't have to read the file
if(*theModDate <= inModDate)
break;
}
theParamLen = 0;
UInt64* theLength = NULL;
theErr = QTSS_GetValuePtr(theFileObject, qtssFlObjLength, 0, (void**)&theLength, &theParamLen);
if (theParamLen != sizeof(UInt64))
break;
if (*theLength > kSInt32_Max)
break;
// Allocate memory for the file data
outData->Ptr = NEW char[ (SInt32) (*theLength + 1) ];
outData->Len = (SInt32) *theLength;
outData->Ptr[outData->Len] = 0;
// Read the data
UInt32 recvLen = 0;
theErr = QTSS_Read(theFileObject, outData->Ptr, outData->Len, &recvLen);
if (theErr != QTSS_NoErr)
{
outData->Delete();
break;
}
Assert(outData->Len == recvLen);
}while(false); // Close the file
if(theFileObject != NULL) {
theErr = QTSS_CloseFileObject(theFileObject);
}
return theErr;
}

之后将QTAccessFile.cpp->AuthorizeRequest中使用到的ReadEntireFile都改为使用ReadEntireFileEx函数。

再次进行推流测试,发现推流一直返回401,这是因为推送库还没有RTSP认证的功能,我们先不做推流验证。因此我们将前面的认证开关由if(false)更改为if(fRequest->GetMethod() != qtssDescribeMethod),也就说只要是推流就跳过认证。(这个地方不太完美,但还找到完美的方法。)

再次进行推流测试,发现这次可以推流成功;然后我们进行拉流测试发现程序又崩溃了,定位到QTSSAccessLogModule.cpp的LogRequest函数中,在此我们将

UInt32 clientBytesRecv = (UInt32)((*rtpBytesSent * (100.0 - *packetLossPercent)) / 100.0);

更改为

UInt32 clientBytesRecv = 0;
if(packetLossPercent != NULL)
clientBytesRecv = (UInt32)((*rtpBytesSent * (100.0 - *packetLossPercent)) / 100.0);

将if (*rtpPacketsSent == 0)更改为if(rtpPacketsSent == NULL || *rtpPacketsSent == 0)

之后再进行测试,发现不会崩溃了,但是一直提示进行RTSP认证,即使我们输入了上面设置的正确用户名和密码还是不行。原来在进行基本认证时不同平台的处理是不一样的,我们通过将如下的代码

#ifdef __Win32__
// The password is md5 encoded for win32
char md5EncodeResult[120];
// no memory is allocated in this function call
MD5Encode(reqPasswdStr, userPasswdStr, md5EncodeResult,sizeof(md5EncodeResult));
if (::strcmp(userPasswdStr, md5EncodeResult) != 0)
authenticated = false;
#else
if (::strcmp(userPasswdStr, (char*)crypt(reqPasswdStr,userPasswdStr)) != 0)
authenticated = false;
#endif 更改为 #if 0
// The password is md5 encoded for win32
char md5EncodeResult[120];
// no memory is allocated in this function call
MD5Encode(reqPasswdStr, userPasswdStr, md5EncodeResult,sizeof(md5EncodeResult));
if (::strcmp(userPasswdStr, md5EncodeResult) != 0)
authenticated = false;
#else
if (::strcmp(userPasswdStr, (char*)crypt(reqPasswdStr,userPasswdStr)) != 0)
authenticated = false;
#endif

我们发现windows下基本验证的时候对密码又进行了md5运算,这不是我们想要的,我们把它改为一致。

再次进行测试,发现还是不行……经排查,发现是对RTSP请求的清理没有到位,将RTSPSession::CleanupRequest由下面代码

void RTSPSession::CleanupRequest()
{
if (fRTPSession != NULL)
{
// Release the ref.
OSRefTable* theMap = QTSServerInterface::GetServer()->GetRTPSessionMap();
theMap->Release(fRTPSession->GetRef()); // NULL out any references to this RTP session
fRTPSession = NULL;
fRoleParams.rtspRequestParams.inClientSession = NULL;
}
if (this->IsLiveSession() == false) //clear out the ID so it can't be re-used.
{
fLastRTPSessionID[0] = 0;
fLastRTPSessionIDPtr.Set(fLastRTPSessionID, 0);
}
if (fRequest != NULL)
{
// Check to see if a filter module has replaced the request. If so, delete
// their request now.
if (fRequest->GetValue(qtssRTSPReqFullRequest) && fInputStream.GetRequestBuffer())
{
if (fRequest->GetValue(qtssRTSPReqFullRequest)->Ptr != fInputStream.GetRequestBuffer()->Ptr)
delete[] fRequest->GetValue(qtssRTSPReqFullRequest)->Ptr;
} // NULL out any references to the current request
//delete fRequest;
//fRequest = NULL;
fRoleParams.rtspRequestParams.inRTSPRequest = NULL;
fRoleParams.rtspRequestParams.inRTSPHeaders = NULL;
} fSessionMutex.Unlock();
fReadMutex.Unlock(); // Clear out our last value for request body length before moving onto the next request
this->SetRequestBodyLength(-1);
}

更改为下面代码

void RTSPSession::CleanupRequest()
{
if (fRTPSession != NULL)
{
// Release the ref.
OSRefTable* theMap = QTSServerInterface::GetServer()->GetRTPSessionMap();
theMap->Release(fRTPSession->GetRef()); // NULL out any references to this RTP session
fRTPSession = NULL;
fRoleParams.rtspRequestParams.inClientSession = NULL;
}
if (this->IsLiveSession() == false) //clear out the ID so it can't be re-used.
{
fLastRTPSessionID[0] = 0;
fLastRTPSessionIDPtr.Set(fLastRTPSessionID, 0);
}
if (fRequest != NULL)
{
// Check to see if a filter module has replaced the request. If so, delete
// their request now.
if (fRequest->GetValue(qtssRTSPReqFullRequest) && fInputStream.GetRequestBuffer())
{
if (fRequest->GetValue(qtssRTSPReqFullRequest)->Ptr != fInputStream.GetRequestBuffer()->Ptr)
delete[] fRequest->GetValue(qtssRTSPReqFullRequest)->Ptr;
} // NULL out any references to the current request
delete fRequest;
fRequest = NULL;
fRoleParams.rtspRequestParams.inRTSPRequest = NULL;
fRoleParams.rtspRequestParams.inRTSPHeaders = NULL;
}
fSessionMutex.Unlock();
fReadMutex.Unlock(); // Clear out our last value for request body length before moving onto the next request
this->SetRequestBodyLength(-1);
}

再次测试,OK!

如有错误,欢迎指正!

获取更多信息

邮件:support@easydarwin.org

WEB:www.EasyDarwin.org

Copyright © EasyDarwin.org 2012-2016

EasyDarwin开源流媒体服务器支持basic基本认证和digest摘要自定义认证的更多相关文章

  1. EasyDarwin开源流媒体服务器支持basic基本认证和digest摘要认证解析

    本文转自EasyDarwin开源团队成员ss的博客:http://blog.csdn.net/ss00_2012/article/details/52262621 RTSP认证作为RTSP标准协议的一 ...

  2. NodeJS版本EasyDarwin开源流媒体服务器开发心得

    title: Node版本EasyDarwin开发心得 date: 2018-03-27 22:46:15 tags: 年后着手Node版本EasyDarwin的开发工作,截止到今天2018年03月2 ...

  3. EasyDarwin开源流媒体服务器Golang版本:服务端录像功能发布

    EasyDarwin开源流媒体服务器(www.easydarwin.org)现在使用Go版本实现了.最新的代码提交,已经支持了推流(或者拉流)的同时进行本地存储. 本地存储的原理,是在推流的同时启动f ...

  4. EasyDarwin开源流媒体服务器Golang版本:拉转推功能之拉流实现方法

    EasyDarwin开源流媒体服务器(www.easydarwin.org),拉转推是一个很有意义的功能,它可将一个独立的RTSP数据源"拉"到服务器,再通过转发协议转发给多个客户 ...

  5. EasyDarwin开源流媒体服务器性能优化之Work-stealing优化方案

    本文转自EasyDarwin开源团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/52400226 EasyDarwin团队的Ba ...

  6. EasyDarwin开源流媒体服务器将select改为epoll的方法

    本文来自EasyDarwin团队Fantasy(fantasy(at)easydarwin.org) 一. EasyDarwin网络模型介绍 EventContext负责监听所有网络读写事件,Even ...

  7. 解决用EasyDarwin开源流媒体服务器做HLS直播时Flash Player卡住的问题

    最近在开发EasyDarwin开源流媒体服务器HLS直播的时候发现一个现象:在PC上用flash player播放HLS和在ios上面播放HLS时,效果明显不同,在ios上播放非常稳定,而在flash ...

  8. EasyDarwin开源流媒体服务器实现RTSP直播同步输出MP4、RTMP、HLS的方案思路

    背景 近期跟开源团队商量,想在EasyDarwin上继续做一些功能扩展,目前EasyDarwin开源流媒体服务器只能够实现高效的RTSP推流直播转发/分发功能,输入与输出都是RTSP/RTP流,不能够 ...

  9. EasyDarwin开源流媒体服务器提供的TS切片/HLS直播打包库

    EasyHLS  Github:https://github.com/EasyDarwin/EasyHLS EasyHLS是什么? EasyHLS是EasyDarwin开源流媒体社区开发的一款HLS打 ...

随机推荐

  1. bq25896 IINDPM 及 無 IINDPM 時的 regsiter

      無 IINDPM status 有 IINDPM status   [bq25890 reg@][0x0]=0x5d[0x1]=0x6[0x2]=0x91[0x3]=0x1a[0x4]=0x8[0 ...

  2. POJ 3468:A Simple Problem with Integers(线段树[成段更新])

    题意:N个数Q次操作.一共两种操作:Q l r :询问[l,r]这个区间里的数字和,C l r c: [l,r]区间里的每个数都加上c.1 ≤ N,Q ≤ 100000. 方法:线段树的成段更新.注意 ...

  3. LeetCode OJ——Remove Duplicates from Sorted List

    http://oj.leetcode.com/problems/remove-duplicates-from-sorted-list/ 链表的去重,要考虑链表的本质. #include <ios ...

  4. 使用 dotnet CLI 来打包和发布 .NET Core nuget package

    原文链接:使用 dotnet CLI 来打包和发布 .NET Core nuget package 如何使用 visual studio 2015/2017 打包和发布 Nuget package, ...

  5. (1)sqlite基础

    一.安装sqlite 下载页面:http://www.sqlite.org/download.html 1.下载 sqlite-tools-win32-*.zip 和 sqlite-dll-win32 ...

  6. sql server 博客

    http://blog.csdn.net/tjvictor/article/category/531421/1 http://blog.csdn.net/zhangqidashu/article/de ...

  7. JavaScript奇技淫巧44招(2)

    JavaScript是一个绝冠全球的编程语言,可用于Web开发.移动应用开发(PhoneGap.Appcelerator).服务器端开发(Node.js和Wakanda)等等.JavaScript还是 ...

  8. 邁向IT專家成功之路的三十則鐵律 鐵律四:IT人快速成長之道-複製

    相信您一定看到過現今有許多各行各業的成功人士,他們最初都是從複製別人的成功經驗開始的,就算是一位知名的歌手,有許多都是在未成名以前,先行模仿知名歌手的唱腔.舞蹈.服裝等等開始的,然後在慢慢經過自我努力 ...

  9. 【jQuery】input textarea 文本变化的动态监听

    实时监听Input textarea文本变化的监听事件:[但不包含通过js动态添加改变的文本事件] HTML: <textarea style="display: none" ...

  10. Ubuntu -- 下如何查看CPU信息, 包括位数和多核信息

    from: http://hi.baidu.com/sdusoul/blog/item/76f349508f74fb6e843524eb.html 查看当前操作系统内核信息# uname -a Lin ...