websocketpp的流媒体微服务使用安全
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了websocketpp的流媒体微服务使用安全相关的知识,希望对你有一定的参考价值。
websocketpp
websocketpp不失为一个具有良好设计的websocket的好使用的lib。我们在使用媒体服务的过程中,可以使用websocketpp制作微服务,提供tcp stream流服务长链接系统,最大的两个问题1 是记录客户端数据,2 是安全问题。这里使用websocket的原因是提供流媒体服务。
鉴权安全性:
first, 客户端需要像鉴权服务器使用用户名和密码
second,客户端必须发送授信flag标识,flag标识为硬件标识,客户端必须首先拿到这个标识,标识为物理隔离,物理隔离概念并不是一定是隔绝,而是带有硬件相关性。
1 如何让websocket服务保证链接的客户是正确的客户,以及是常用的客户,常用的意思是一般一直是这些客户。服务端接收的用户名和密码是添加进去的,内部系统在每隔一定的事件修改密码,服务端自动化做更改当然也是可以的,只有内部人员可以知道密码更改。客户端在登录以后获取到用户名和密码的变种ID,用来传输到ws服务
2 授信flag和硬件相关,客户端在配置文件或者html里面可以直接更改,外部系统复制flag是没有用的,除非同时获取了用户名和密码,但是服务器会记录所有登录的ip和端口等等相关客户端数据。
1 如何记录客户端数据
不像javascript数据,数据结构可以随时改变,c++的数据结构需要在编译时确定,当然我们可以使用配置或者脚本语言来做一些灵活的配置,有一个问题,客户端链接上来后,我们如何标记客户端,记录一些客户端船上来的值?有三种方式,a) 自己准备一个hash,每次存和取都从hash里面进行
map<connection, data>
数据结构data 就是我们需要的,比如
struct connection_data
enum_flag m_flag = enum_null;
std::string m_app;
std::string m_id;
std::string m_user;
//第几个线程
int m_nthread = -1;
std::string m_address;
uint32_t m_heart = 0;
;
这个方法的问题是每次都要去从hash表里找。
b)直接修改源代码,这个没有大问题,代码你掌握,直接加在源码里面,这种暴力方法是可行的。
c) 实际上,本身websocketpp就提供了这种方法,server 本身就是一个模板类,在声明的时候把connection_data 放到模板参数里面
struct custom_config : public websocketpp::config::asio
// pull default settings from our core config
typedef websocketpp::config::asio core;
typedef core::concurrency_type concurrency_type;
typedef core::request_type request_type;
typedef core::response_type response_type;
typedef core::message_type message_type;
typedef core::con_msg_manager_type con_msg_manager_type;
typedef core::endpoint_msg_manager_type endpoint_msg_manager_type;
typedef core::alog_type alog_type;
typedef core::elog_type elog_type;
typedef core::rng_type rng_type;
typedef core::transport_type transport_type;
typedef core::endpoint_base endpoint_base;
// Set a custom connection_base class
typedef connection_data connection_base;
;
typedef websocketpp::server<custom_config> c_server;
这样,我们在使用的时候,事件on_open中
void c_ws_server::on_open(connection_hdl hdl)
std::error_code ec;
c_server::connection_ptr connptr = m_server.get_con_from_hdl(hdl,ec);
if (connptr == nullptr || ec)
if(ec)
std::cout << "exception: " << ec.message() << std::endl;
return;
std::string &url = connptr->get_uri()->str();
if (judge_open(url, connptr) == 0)
//这里可以直接使用
connptr->m_id = url;
connptr->m_address = connptr->get_remote_endpoint();
//connptr->get_re
m_proxy->node_into(connptr);
2、鉴权问题
服务器只能被一些用户访问,同时限制客户端个数,客户端的url必须传输这些密文。
int c_ws_server::judge_open(std::string &url, c_server::connection_ptr ptr)
s_url surl;
std::map<string, string> paras;
split_url(url, surl, paras);
if (surl.m_app == NULL || surl.m_id == NULL || surl.m_para == NULL)
close(-1, ptr);
return -1;
auto iter_user = paras.find("user");
if (iter_user == paras.end())
close(-2, ptr);
return -2;
auto iter_flag = paras.find("flag");
if (iter_flag == paras.end())
close(-3, ptr);
return -3;
ptr->m_app = surl.m_app;
ptr->m_id = surl.m_id;
ptr->m_user = iter_user->second;
ptr->m_flag = iter_flag->second.compare("pull") == 0 ? enum_wpull_join : enum_wpush_join;
//成功
return 0;
我们在onopen事件中要求ws 客户端传入权限,这个对应的码进行加密后传入后台,后台对应解码的flag 需要解成受信服务器id 和服务器的记录id进行对应,否则就无法链接,链接直接被断开,用户的用户名和密码同时被传输到服务端,所以客户端是需要在传输url之前问鉴权服务要到用户名和密码。
安全问题和漏洞:
如果事先复制授信flag 可行否?
答:
授信flag 是和服务器端的硬件相对应的,各个服务器端的资源并不相同,流并不一样,所以保证了资源安全和授信flag的独立性。
3、websocket html端模拟
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">运行 WebSocket</a>
</div>
<div id="imgDiv"></div>
<script type="text/javascript">
function WebSocketTest()
if ("WebSocket" in window)
// alert("您的浏览器支持 WebSocket!");
// 打开一个 web socket
var ws = new WebSocket("ws://127.0.0.1:9002/live/1001?flag=s&user=qianbo");
console.log(ws);
ws.onopen = function (evt)
// Web Socket 已连接上,使用 send() 方法发送数据
console.log("connected")
let obj = JSON.stringify(
type: 'login',
data:
roomId: '1',
userId: '2',
userName: 'lrc2'
)
ws.send(obj);
//alert("数据发送中...");
;
ws.onmessage = function (evt)
if (typeof (evt.data) == "string")
//textHandler(JSON.parse(evt.data));
else
var reader = new FileReader();
reader.onload = function (evt)
if (evt.target.readyState == FileReader.DONE)
var url = evt.target.result;
console.log(url);
//img.src = url;// "bg1.jpg";
var img = document.getElementById("imgDiv");
img.innerHTML = "<img src = " + url + " />";
reader.readAsDataURL(evt.data);
;
ws.onclose = function ()
// 关闭 websocket
alert("连接已关闭...");
;
else
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
</script>
</body>
</html>
数据库记录信息
使用sqlite来记录所有客户端信息,sqlite本身为进程内服务器,不受网络攻击
头文件
#ifndef _DB_SET_SQLITE_
#define _DB_SET_SQLITE_
#include <string>
using namespace std;
typedef struct db_content
string key;
string app;
string channel;
string route;
string content;
string push_time;
db_content;
void *db_open(const char *db_path);
void db_close(void **db_handle);
enum API_OP_GET, API_OP_SET, API_OP_DEL ;
void db_op(struct mg_connection *nc, const struct http_message *hm,
const struct mg_str *key, void *db, int op);
#endif
cpp
#include "dbstorage.h"
#include "sqlite3.h"
#include <time.h>
void time_get(char * buf)
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
strcpy(buf, asctime(timeinfo));
void *db_open(const char *db_path)
sqlite3 *db = NULL;
if (sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
SQLITE_OPEN_FULLMUTEX,
NULL) == SQLITE_OK)
char *sql = "CREATE TABLE IF NOT EXISTS log([key] TEXT, [val] TEXT,[category] TEXT, [infotime] TimeStamp NOT NULL DEFAULT(datetime('now', 'localtime'))";
sqlite3_exec(db, sql, 0, 0, 0);
return db;
void db_close(void **db_handle)
if (db_handle != NULL && *db_handle != NULL)
sqlite3_close((sqlite3*)*db_handle);
*db_handle = NULL;
#define str(x) x.c_str()
#define len(x) strlen(str(x))
#define start(n) int n = 0
#define sqlbindstr(name,n) n++;sqlite3_bind_text(stmt,n,str(name), len(name), SQLITE_STATIC)
static void op_set(db_content * cn, sqlite3 *db)
sqlite3_stmt *stmt = NULL;
//time_get(time);
if (sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO info VALUES (?, ?, ? ,? ,? ,?);", -1,
&stmt, NULL) == SQLITE_OK)
start(n);
sqlbindstr(cn->key, n);
sqlbindstr(cn->app, n);
sqlbindstr(cn->channel, n);
sqlbindstr(cn->route, n);
sqlbindstr(cn->content, n);
sqlbindstr(cn->push_time, n);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
static db_content *op_get(char * key, sqlite3 *db)
sqlite3_stmt *stmt = NULL;
const char *data = NULL;
int result;
if (sqlite3_prepare_v2(db, "SELECT app,channel,route,content,push_time FROM info WHERE key = ?;", -1, &stmt,
NULL) == SQLITE_OK)
sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_STATIC);
result = sqlite3_step(stmt);
db_content *pdcn = new db_content();
if ((result == SQLITE_OK || result == SQLITE_ROW))
char *data = (char *)sqlite3_column_text(stmt, 0);
if (data != NULL)
pdcn->app = data;
data = (char *)sqlite3_column_text(stmt, 1);
if (data != NULL)
pdcn->channel = data;
data = (char *)sqlite3_column_text(stmt, 2);
if (data != NULL)
pdcn->route = data;
data = (char *)sqlite3_column_text(stmt, 3);
if (data != NULL)
pdcn->content = data;
data = (char *)sqlite3_column_text(stmt, 4);
if (data != NULL)
pdcn->push_time = data;
else
return NULL;
sqlite3_finalize(stmt);
else
return NULL;
static int op_del(string key, void *db)
sqlite3_stmt *stmt = NULL;
int result;
sqlite3 *dbs = (sqlite3*)(db);
if (sqlite3_prepare_v2(dbs, "DELETE FROM info WHERE key = ?;", -1, &stmt,
NULL) == SQLITE_OK)
sqlite3_bind_text(stmt, 1, key.c_str(), strlen(key.c_str()), SQLITE_STATIC);
result = sqlite3_step(stmt);
if (result == SQLITE_OK || result == SQLITE_ROW)
else
sqlite3_finalize(stmt);
return 0;
else
return -1;
//mg_printf(nc, "%s",
// "HTTP/1.1 500 Server Error\\r\\n"
// "Content-Length: 0\\r\\n\\r\\n");
void db_op(struct mg_connection *nc, const struct http_message *hm,
const struct mg_str *key, void *db, int op)
switch (op)
case API_OP_GET:
//op_get(nc, hm, key, db);
break;
case API_OP_SET:
//op_set(nc, hm, key, db);
break;
case API_OP_DEL:
//op_del(nc, hm, key, db);
break;
default:
/* mg_printf(nc, "%s",
"HTTP/1.0 501 Not Implemented\\r\\n"
"Content-Length: 0\\r\\n\\r\\n");*/
break;
分析到此结束,读者有兴趣可以加入作者一起讨论
以上是关于websocketpp的流媒体微服务使用安全的主要内容,如果未能解决你的问题,请参考以下文章
Day916.基于JWT令牌的安全认证架构 -SpringBoot与K8s云原生微服务实践
Day915.安全认证架构演进:微服务阶段 -SpringBoot与K8s云原生微服务实践