怎么在srs代码中添加token认证
时间: 2025-07-22 07:23:49 浏览: 15
<think>我们正在讨论SRS(Simple-RTMP-Server)流媒体服务器的Token认证机制集成。用户希望在其SRS代码中实现Token身份验证功能。根据之前的历史记录,我们讨论过Linux下统计代码行数的方法,但当前问题已转向SRS服务器的开发。
SRS是一个开源的RTMP/HLS/WebRTC/SRT/GB28181流媒体服务器,其源代码位于GitHub:https://github.com/ossrs/sns
在SRS中集成Token认证机制,通常需要在客户端连接时(如推流或拉流)进行验证。常见的做法是在推流地址或播放地址中加入token参数,服务器端对该token进行验证。
以下是实现步骤:
1. **确定验证点**:SRS中,推流验证点通常在`on_publish`事件,拉流验证点在`on_play`事件。我们可以在这两个事件中插入token验证逻辑。
2. **Token生成规则**:需要定义token的生成规则,例如使用时间戳、密钥和过期时间等,通过一定的算法(如MD5、SHA1或HMAC)生成token。客户端在推流或拉流时携带token。
3. **修改SRS代码**:
- 在`trunk/src/app/srs_app_rtmp_conn.cpp`中,找到`do_publishing`方法(推流)和`playing`方法(拉流)进行修改。
- 或者,在`trunk/src/app/srs_app_source.cpp`中,在`on_publish`和`on_play`事件处理中增加验证。
4. **获取Token参数**:从RTMP连接请求的URL中解析出token参数。例如,推流地址为:rtmp://server/app/stream?token=xxx,需要从请求中提取出token。
5. **验证逻辑**:将客户端传递的token与服务器生成的期望token进行比对,或者验证token的合法性(如是否过期,签名是否正确)。
6. **返回错误**:如果验证失败,则断开连接并返回错误。
下面我们详细说明代码修改步骤:
### 1. 修改推流验证(on_publish)
在`trunk/src/app/srs_app_rtmp_conn.cpp`中,找到`SrsRtmpConn::do_publishing`方法,在开始推流之前进行验证。
```cpp
// 在do_publishing方法中,找到以下代码:
// if ((ret = srs_rtmp_start_publish(skt, res->stream)) != ERROR_SUCCESS) {
// return ret;
// }
// 在开始推流之前,加入token验证
// 获取请求中的token
std::string token = req->get_query("token");
if (token.empty()) {
return ERROR_RTMP_PUBLISH_TOKEN_MISSING;
}
// 验证token,假设有一个验证函数
if (!TokenValidator::validate(token, req->app, req->stream)) {
return ERROR_RTMP_PUBLISH_TOKEN_INVALID;
}
```
### 2. 修改拉流验证(on_play)
在`trunk/src/app/srs_app_rtmp_conn.cpp`中,找到`SrsRtmpConn::playing`方法,在开始播放之前进行验证。
```cpp
// 在playing方法中,找到开始播放的位置之前
// 获取请求中的token
std::string token = req->get_query("token");
if (token.empty()) {
return ERROR_RTMP_PLAY_TOKEN_MISSING;
}
// 验证token
if (!TokenValidator::validate(token, req->app, req->stream)) {
return ERROR_RTMP_PLAY_TOKEN_INVALID;
}
```
### 3. 实现TokenValidator
我们需要实现一个Token验证工具类。创建文件`trunk/src/app/srs_app_token.hpp`和`trunk/src/app/srs_app_token.cpp`。
**srs_app_token.hpp**
```cpp
#ifndef SRS_APP_TOKEN_HPP
#define SRS_APP_TOKEN_HPP
#include <string>
class TokenValidator {
public:
static bool validate(const std::string& token, const std::string& app, const std::string& stream);
};
#endif
```
**srs_app_token.cpp**
```cpp
#include "srs_app_token.hpp"
#include <sstream>
#include <openssl/md5.h>
#include <srs_kernel_utility.hpp>
// 假设使用MD5签名,密钥为"secret_key"
#define SECRET_KEY "secret_key"
bool TokenValidator::validate(const std::string& token, const std::string& app, const std::string& stream) {
// 生成期望的token:格式为 md5(SECRET_KEY + app + stream + timestamp) + timestamp
// 实际中可能还包含过期时间,这里简化处理
// 从token中提取时间戳(假设最后10位是时间戳)
if (token.length() < 10) {
return false;
}
std::string timestamp_str = token.substr(token.length() - 10);
int64_t timestamp = ::atol(timestamp_str.c_str());
// 检查时间戳是否过期(假设有效期为60秒)
if (srs_get_system_time() - timestamp > 60 * 1000) {
return false;
}
// 生成期望的token值
std::stringstream ss;
ss << SECRET_KEY << app << stream << timestamp;
unsigned char md[MD5_DIGEST_LENGTH];
MD5((unsigned char*)ss.str().c_str(), ss.str().length(), md);
// 将md5转换成16进制字符串
char buf[33];
for (int i = 0; i < 16; i++) {
sprintf(buf + i*2, "%02x", md[i]);
}
buf[32] = '\0';
std::string expected_token = std::string(buf) + timestamp_str;
// 比较token
return token == expected_token;
}
```
### 4. 错误码定义
在`trunk/src/app/srs_app_error.hpp`中定义错误码:
```cpp
// 推流token相关错误
#define ERROR_RTMP_PUBLISH_TOKEN_MISSING 2000
#define ERROR_RTMP_PUBLISH_TOKEN_INVALID 2001
// 拉流token相关错误
#define ERROR_RTMP_PLAY_TOKEN_MISSING 2002
#define ERROR_RTMP_PLAY_TOKEN_INVALID 2003
```
### 5. 修改Makefile
将新文件加入编译,修改`trunk/objs/Makefile`,在对应的位置添加`srs_app_token.cpp`。
### 6. 测试
编译并重启SRS,使用推流地址和拉流地址中加入token参数进行测试。
### 注意事项
- 上述实现中,token的生成规则是:`token = md5(secret_key + app + stream + timestamp) + timestamp`,其中timestamp为当前时间戳(毫秒级),共10位数字。客户端在推流或拉流时需要按照相同规则生成token。
- 实际应用中,密钥需要配置在配置文件中,而不是硬编码。
- 需要处理URL中特殊字符的情况,但SRS的请求解析已经处理了URL解码。
### 示例推流地址
```
rtmp://server/app/stream?token=7f5e8d7a9e6f5d4e3c2b1a0f12345678901234567
```
其中,`7f5e8d7a9e6f5d4e3c2b1a0f`是MD5的前24位(实际32位,这里简写),`1234567890`是10位时间戳。
### 改进方向
1. 支持配置:通过配置文件开启/关闭token验证,并配置密钥和有效期。
2. 支持多种算法:如SHA1、HMAC等。
3. 支持更复杂的规则,如IP绑定等。
以上是基本的实现步骤。由于SRS版本不同,代码结构可能略有差异,请根据实际代码调整。
§§相关问题§§
1. 如何在SRS中配置Token认证的密钥和有效期?
2. SRS支持哪些类型的Token认证(如推流、拉流、API)?
3. 如何为SRS的HTTP-FLV和HLS播放也添加Token认证?
4. 在SRS中集成JWT Token认证有哪些注意事项?</think>在 SRS (Simple-RTMP-Server) 中实现 Token 身份验证功能,需要在服务器处理连接请求时添加认证机制。以下是完整的实现步骤和代码示例:
### 一、实现原理
1. **认证时机**:在客户端发起推流(publish)或播放(play)请求时进行验证
2. **Token 生成规则**:`Token = MD5(SecretKey + App + Stream + Timestamp + Nonce)`
3. **验证流程**:
```mermaid
graph TD
A[客户端请求] --> B{URL包含token?}
B -->|是| C[验证token有效性]
B -->|否| D[拒绝连接]
C -->|有效| E[允许流传输]
C -->|无效| D
```
### 二、核心代码实现
在 `src/app/srs_app_rtmp_conn.cpp` 中添加验证逻辑:
```cpp
// 添加在文件头部
#include <openssl/md5.h>
#include <sstream>
// Token 验证函数
bool verify_token(std::string app, std::string stream, std::string token) {
// 1. 从配置获取密钥
std::string secret_key = _srs_config->get_token_secret();
// 2. 解析token中的时间戳
int64_t timestamp = std::stoll(token.substr(0, 13));
// 3. 检查时间有效性(30秒内有效)
int64_t current_time = srs_get_system_time();
if (abs(current_time - timestamp) > 30000) {
return false; // 超时
}
// 4. 重新计算签名
std::string data = secret_key + app + stream + std::to_string(timestamp);
unsigned char digest[MD5_DIGEST_LENGTH];
MD5((unsigned char*)data.c_str(), data.length(), digest);
// 5. 比较签名
char md5_str[33];
for(int i=0; i<16; i++) sprintf(&md5_str[i*2], "%02x", digest[i]);
return token.substr(13) == std::string(md5_str);
}
// 在 do_publishing() 方法中添加验证
int SrsRtmpConn::do_publishing() {
// ...原有代码...
// 添加Token验证
std::string token = req->query_get("token");
if (token.empty() || !verify_token(req->app, req->stream, token)) {
return srs_error_new(ERROR_RTMP_TOKEN_INVALID, "Invalid token");
}
// ...继续原有流程...
}
```
### 三、配置支持
在 `src/app/srs_app_config.hpp` 中添加配置项:
```cpp
class SrsConfDirective {
public:
// 添加token密钥配置
std::string get_token_secret() {
return get_string("token_secret", "default_secret_key");
}
};
```
在配置文件中启用:
```nginx
# conf/srs.conf
vhost __defaultVhost__ {
# 开启Token验证
token_auth on;
token_secret your_secure_key_123;
}
```
### 四、客户端使用示例
推流地址格式:
```
rtmp://server/app/stream?token=1680000000000c4ca4238a0b923820dcc509a6f75849b
```
其中:
1. `1680000000000`:13位时间戳(毫秒)
2. `c4ca4238a0b923820dcc509a6f75849b`:MD5(secret_key + app + stream + timestamp)
### 五、安全增强建议
1. **HTTPS 支持**:
```cpp
// 在握手阶段强制HTTPS
if (!req->tcUrl.starts_with("https://")) {
return srs_error_new(ERROR_RTMP_INSECURE_REQUEST, "HTTPS required");
}
```
2. **动态密钥轮换**:实现 `get_token_secret()` 时从密钥管理服务动态获取
3. **防止重放攻击**:
```cpp
// 添加nonce验证
static std::map<std::string, int64_t> used_nonces;
bool is_replay = used_nonces.find(nonce) != used_nonces.end();
used_nonces[nonce] = srs_get_system_time();
```
### 六、测试验证
使用 SRS 测试工具验证:
```bash
# 启动带Token验证的SRS
./objs/srs -c conf/token.conf
# 测试有效Token
ffmpeg -re -i input.mp4 -c copy -f flv \
"rtmp://localhost/live/stream?token=$(generate_token)"
# 测试无效Token(应返回错误)
ffmpeg -re -i input.mp4 -c copy -f flv \
"rtmp://localhost/live/stream?token=invalid_token"
```
> **注意**:完整实现需同时在 `do_playing()` 方法中添加播放验证,逻辑与推流验证类似[^1]。
阅读全文
相关推荐










