[feat] use sqllite3 to manager device id and password

This commit is contained in:
dijunkun
2025-06-19 17:43:29 +08:00
parent 721084923a
commit c38f404ef3
7 changed files with 188 additions and 40 deletions

View File

@@ -1,9 +0,0 @@
#include "client_id_generator.h"
ClientIdGenerator::ClientIdGenerator() {}
ClientIdGenerator::~ClientIdGenerator() {}
std::string ClientIdGenerator::GeneratorNewId() {
return std::to_string(++base_id_);
}

View File

@@ -1,25 +0,0 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-08-06
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _CLIENT_ID_GENERATOR_H_
#define _CLIENT_ID_GENERATOR_H_
#include <iostream>
#include <string>
class ClientIdGenerator {
public:
ClientIdGenerator();
~ClientIdGenerator();
public:
std::string GeneratorNewId();
private:
int base_id_ = 300000000;
};
#endif

View File

@@ -0,0 +1,137 @@
#include "device_db_manager.h"
#include <openssl/sha.h>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>
#include "log.h"
DeviceDBManager::DeviceDBManager(const std::string& dbPath) : db(nullptr) {
if (sqlite3_open(dbPath.c_str(), &db) != SQLITE_OK) {
LOG_ERROR("Failed to open database: {} with error msg {}", dbPath,
sqlite3_errmsg(db));
}
initDB();
}
DeviceDBManager::~DeviceDBManager() {
if (db) sqlite3_close(db);
}
void DeviceDBManager::initDB() {
const char* sql =
"CREATE TABLE IF NOT EXISTS devices ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"device_id TEXT UNIQUE NOT NULL,"
"password_hash TEXT NOT NULL);";
char* errMsg = nullptr;
int rc = sqlite3_exec(db, sql, nullptr, nullptr, &errMsg);
if (rc != SQLITE_OK) {
LOG_ERROR("Failed to initialize DB: {}", errMsg);
sqlite3_free(errMsg);
}
}
std::string DeviceDBManager::sha256(const std::string& str) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char*>(str.c_str()), str.size(), hash);
std::stringstream ss;
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
return ss.str();
}
std::string DeviceDBManager::generateDeviceId() {
static std::mt19937 rng(static_cast<unsigned>(
std::chrono::steady_clock::now().time_since_epoch().count()));
std::uniform_int_distribution<int> dist(0, 9);
std::string id;
for (int i = 0; i < 9; ++i) {
id += '0' + dist(rng);
}
return id;
}
std::string DeviceDBManager::addDevice(const std::string& password) {
std::string hash = sha256(password);
const int maxTry = 10;
for (int i = 0; i < maxTry; ++i) {
std::string deviceId = generateDeviceId();
const char* sql =
"INSERT INTO devices (device_id, password_hash) VALUES (?, ?);";
sqlite3_stmt* stmt = nullptr;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
LOG_ERROR("Failed to prepare insert statement");
return "";
}
sqlite3_bind_text(stmt, 1, deviceId.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, hash.c_str(), -1, SQLITE_TRANSIENT);
int rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
if (rc == SQLITE_DONE) {
return deviceId;
} else if (rc == SQLITE_CONSTRAINT) {
continue;
} else {
LOG_ERROR("Failed to insert device: {}", sqlite3_errmsg(db));
return "";
}
}
LOG_ERROR("Failed to generate unique device ID after {} attempts.", maxTry);
return "";
}
bool DeviceDBManager::verifyDevice(const std::string& deviceId,
const std::string& password) {
std::string hash = sha256(password);
const char* sql =
"SELECT COUNT(*) FROM devices WHERE device_id = ? AND password_hash = ?;";
sqlite3_stmt* stmt = nullptr;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
LOG_ERROR("Failed to prepare verify statement.");
return false;
}
sqlite3_bind_text(stmt, 1, deviceId.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, hash.c_str(), -1, SQLITE_TRANSIENT);
bool found = false;
if (sqlite3_step(stmt) == SQLITE_ROW) {
int count = sqlite3_column_int(stmt, 0);
found = (count > 0);
}
sqlite3_finalize(stmt);
return found;
}
bool DeviceDBManager::removeDevice(const std::string& deviceId) {
const char* sql = "DELETE FROM devices WHERE device_id = ?;";
sqlite3_stmt* stmt = nullptr;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
LOG_ERROR("Failed to prepare delete statement.");
return false;
}
sqlite3_bind_text(stmt, 1, deviceId.c_str(), -1, SQLITE_TRANSIENT);
bool success = (sqlite3_step(stmt) == SQLITE_DONE);
sqlite3_finalize(stmt);
return success;
}

View File

@@ -0,0 +1,37 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-06-19
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _DEVICE_DB_MANAGER_H_
#define _DEVICE_DB_MANAGER_H_
#include <sqlite3.h>
#include <string>
class DeviceDBManager {
public:
explicit DeviceDBManager(const std::string& dbPath);
~DeviceDBManager();
DeviceDBManager(const DeviceDBManager&) = delete;
DeviceDBManager& operator=(const DeviceDBManager&) = delete;
public:
std::string addDevice(const std::string& password);
bool verifyDevice(const std::string& deviceId, const std::string& password);
bool removeDevice(const std::string& deviceId);
private:
std::string sha256(const std::string& str);
void initDB();
std::string generateDeviceId();
private:
sqlite3* db;
};
#endif // _DEVICE_DB_MANAGER_H_

View File

@@ -47,6 +47,8 @@ SignalServer::~SignalServer() {}
bool SignalServer::on_open(websocketpp::connection_hdl hdl) {
ws_connections_[hdl] = ws_connection_id_++;
device_db_manager_ = std::make_unique<DeviceDBManager>("");
return true;
}
@@ -155,7 +157,7 @@ void SignalServer::on_message(websocketpp::connection_hdl hdl,
case "login"_H: {
std::string host_id = j["user_id"].get<std::string>();
if (host_id.empty()) {
host_id = client_id_generator_.GeneratorNewId();
host_id = ""; // todo
LOG_INFO("New client, assign id [{}] to it", host_id);
}

View File

@@ -9,7 +9,7 @@
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include "client_id_generator.h"
#include "device_db_manager.h"
#include "transmission_manager.h"
using nlohmann::json;
@@ -48,7 +48,7 @@ class SignalServer {
private:
TransmissionManager transmission_manager_;
ClientIdGenerator client_id_generator_;
std::unique_ptr<DeviceDBManager> device_db_manager_;
};
#endif

View File

@@ -6,7 +6,7 @@ set_languages("c++17")
add_rules("mode.release", "mode.debug")
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0")
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.14.1", "sqlite3 3.49.0", "openssl")
add_defines("ASIO_STANDALONE", "ASIO_HAS_STD_TYPE_TRAITS",
"ASIO_HAS_STD_SHARED_PTR", "ASIO_HAS_STD_ADDRESSOF", "ASIO_HAS_STD_ATOMIC",
@@ -22,7 +22,7 @@ elseif is_os("linux") then
set_config("cxxflags", "-fPIC")
end
add_packages("spdlog")
add_packages("spdlog", "openssl", "sqlite3")
includes("thirdparty")
@@ -36,9 +36,15 @@ target("common")
set_kind("headeronly")
add_includedirs("src/common", {public = true})
target("device_db_manager")
set_kind("object")
add_deps("log")
add_files("src/device_db_manager/*.cpp")
add_includedirs("src/device_db_manager", {public = true})
target("signal_server")
set_kind("binary")
add_deps("log", "common")
add_deps("log", "common", "device_db_manager")
add_files("src/*.cpp")
add_packages("asio", "nlohmann_json", "spdlog")
add_includedirs("thirdparty/websocketpp/include")