http://www.infocool.net/kb/WWW/201612/246490.html

来源:互联网 发布:ubuntu 中英字体不同 编辑:程序博客网 时间:2024/05/16 01:08

如何给 nginx rtmp 服务加入鉴权机制

来源:CSDN   发布时间: 2016-12-10   作者:cui918   浏览次数:199 
摘要: 目前直播平台非常的火爆。当前有不少的流媒体CDN,基于rtmp,http-flv和hls协议的。 也可以自己搭建私有的流媒体服务器,目前...


目前直播平台非常的火爆。当前有不少的流媒体CDN,基于rtmp,http-flv和hls协议的。

也可以自己搭建私有的流媒体服务器,目前比较常见的有:Live555,EasyDarwin,Red5,DSS,Wowza,nginx-rtmp

这里重点讲解开源的nginx-rtmp服务器。但是流媒体服务器最大的一个问题就是防盗链和鉴权问题,如何防止流媒体服务器被第三方应用免费使用。

所以需要给nginx-rtmp添加鉴权机制,大致的方案如下:

启动一个鉴权服务。提供 get_user_token 和 auth 两个api。

给每个主播或者观看直播的人员分配user id 和 token,当填写推流和拉流url需要填写鉴权参数,比如:


推流地址:

格式:推流名称?用户ID&用户Token

如何给 nginx rtmp 服务加入鉴权机制

拉流地址:

rtmp://192.168.5.238:1982/mytv/abc123?1&1481169468_586597


先下载nginx和nginx-rtmp-module源代码

https://www.nginx.com/

https://github.com/arut/nginx-rtmp-module


在nginx-rtmp-module中的ngx_rtmp_cmd_module.c添加鉴权方法:

int auth(const char *auth_info){int ret = 0;int fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in address; bzero(&address,sizeof(address));  address.sin_family=AF_INET;  address.sin_addr.s_addr=inet_addr("127.0.0.1");address.sin_port=htons(8888);  //设置读写操作超时时间struct timeval tv;tv.tv_sec = 3;tv.tv_usec = 0;setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));ret = connect(fd, (struct sockaddr *)&address, sizeof(address));if(ret != 0){ngx_log_stderr(0, "connect to auth svr failed, fd:%d, ret:%d, errno:%d, errmsg:%s\n", fd, ret, errno, strerror(errno));close(fd);return ret;}ngx_log_stderr(0, "connect to auth svr success!\n");char *pos = NULL;pos = strchr(auth_info, '&');if(pos == NULL){ngx_log_stderr(0, "auth_info is invalid.\n");close(fd);return -1;}char user_id[100] = {0};char token[100] = {0};strncpy(user_id, auth_info, (unsigned int)(pos-auth_info));strncpy(token, pos+1, (unsigned int)(auth_info+strlen(auth_info)-pos-1));char req[1024] = {0};snprintf(req, 1024, "{\"head\":{\"cmd\":\"auth\", \"time\":%llu}, \"body\":{\"user_id\":%s, \"token\":\"%s\"}}\n",(unsigned long long)time(NULL), user_id, token);ret = send(fd, req, strlen(req), 0);if(ret > 0){ngx_log_stderr(0, "send auth req success!\n");}else{ngx_log_stderr(0, "send auth req failed!, ret:%d\n", ret);close(fd);return -1;}char rsp[1024] = {0};ret = recv(fd, rsp, 1023, 0);if(ret > 0 ){ngx_log_stderr(0, "rsp from auth svr:%s\n", rsp);char *pos = NULL;pos = strstr(rsp, "err");if(pos == NULL){ngx_log_stderr(0, "rsq is invalid.\n");close(fd);return -1;}char err = *(pos+5);if(err == '0'){ngx_log_stderr(0, "auth success!\n");close(fd);return 0;}else{ngx_log_stderr(0, "auth failed!\n");close(fd);return -1;}}else if(ret == 0){ngx_log_stderr(0, "socket is colse!\n");close(fd);return -1;}else{ngx_log_stderr(0, "recv failed, ret:%d\n", ret);}close(fd);return ret;}

将鉴权方法在nginx-rtmp-module中的ngx_rtmp_cmd_module.c的推流初始化方法(如下)

static ngx_int_t ngx_rtmp_cmd_publish_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in)


调用鉴权方法,具体代码如下:

static ngx_int_tngx_rtmp_cmd_publish_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,        ngx_chain_t *in){    static ngx_rtmp_publish_t       v;    static ngx_rtmp_amf_elt_t      in_elts[] = {        /* transaction is always 0 */        { NGX_RTMP_AMF_NUMBER,          ngx_null_string,          NULL, 0 },        { NGX_RTMP_AMF_NULL,          ngx_null_string,          NULL, 0 },        { NGX_RTMP_AMF_STRING,          ngx_null_string,          &v.name, sizeof(v.name) },        { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_STRING,          ngx_null_string,          &v.type, sizeof(v.type) },    };    ngx_memzero(&v, sizeof(v));    if (ngx_rtmp_receive_amf(s, in, in_elts,                             sizeof(in_elts) / sizeof(in_elts[0])))    {        return NGX_ERROR;    }    ngx_rtmp_cmd_fill_args(v.name, v.args);    ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,                  "publish: name='%s' args='%s' type=%s silent=%d",                  v.name, v.args, v.type, v.silent);    int ret = auth((const char*)v.args);    if(ret != 0)    {ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "auth failed!");return NGX_ERROR;    }    return ngx_rtmp_publish(s, &v);}

在播放回调函数中也要调用鉴权方法,具体代码如下:

static ngx_int_tngx_rtmp_cmd_play_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,        ngx_chain_t *in){    static ngx_rtmp_play_t          v;    static ngx_rtmp_amf_elt_t       in_elts[] = {        /* transaction is always 0 */        { NGX_RTMP_AMF_NUMBER,          ngx_null_string,          NULL, 0 },        { NGX_RTMP_AMF_NULL,          ngx_null_string,          NULL, 0 },        { NGX_RTMP_AMF_STRING,          ngx_null_string,          &v.name, sizeof(v.name) },        { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_NUMBER,          ngx_null_string,          &v.start, 0 },        { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_NUMBER,          ngx_null_string,          &v.duration, 0 },        { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_BOOLEAN,          ngx_null_string,          &v.reset, 0 }    };    ngx_memzero(&v, sizeof(v));    if (ngx_rtmp_receive_amf(s, in, in_elts,                             sizeof(in_elts) / sizeof(in_elts[0])))    {        return NGX_ERROR;    }    ngx_rtmp_cmd_fill_args(v.name, v.args);    ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,                  "play: name='%s' args='%s' start=%i duration=%i "                  "reset=%i silent=%i",                  v.name, v.args, (ngx_int_t) v.start,                  (ngx_int_t) v.duration, (ngx_int_t) v.reset,                  (ngx_int_t) v.silent);    int ret = auth((const char*)v.args);    if(ret != 0)    {        ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "auth failed!");        return NGX_ERROR;    }    return ngx_rtmp_play(s, &v);}

重新编译nginx源代码

#./configure --prefix=/home/xucuiping/tool/src/nginx_self/sdk  --add-module=/home/xucuiping/tool/src/nginx_self/src/nginx-rtmp-module-master   --with-debug

#make

#make install


启动nginx

#./nginx -c /home/xucuiping/tool/src/nginx_self/sdk/conf/nginx_rtmp.conf


测试:

通过OBS推流和RTMP播放器测试验证。

0 0
原创粉丝点击