1、完善注冊/登錄
1. 涉及的數據庫表單:user_info
2. 引用MySQL線程池,Redis線程池
3. 完善注冊功能
4. 完善登錄功能
2.1 涉及的數據庫表單:user_info
重新創建數據庫
#創建數據庫
DROP DATABASE IF EXISTS `0voice_tuchuang`;CREATE DATABASE `0voice_tuchuang`;
#使用數據庫
use `0voice_tuchuang`;
先清楚user表(如果存在) ,然后重新創建表單
# 清除表單
DROP TABLE IF EXISTS `user_info`;
# 重新創建表單 id自增,nick_name user_name創建唯一索引
CREATE TABLE `user_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用戶序號,自動遞增,主鍵',`user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用戶名稱',`nick_name` varchar(32) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT '用戶昵稱',`password` varchar(32) NOT NULL DEFAULT '' COMMENT '密碼',`phone` varchar(16) NOT NULL DEFAULT '' COMMENT '手機號碼',`email` varchar(64) DEFAULT '' COMMENT '郵箱',`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '時間',PRIMARY KEY (`id`),UNIQUE KEY `uq_nick_name` (`nick_name`),UNIQUE KEY `uq_user_name` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='用戶信息表';
這樣就新增了用戶表單。
2.2 引入MySQL線程池,Redis線程池
redis mysql目錄為MySQL redis操作接口。
MySQL連接池,目前設置了master,slave兩個連接池,master用于有寫的操作,slave用于讀的操作,通 過指定連接池獲取連接示例代碼:
CDBManager *db_manager = CDBManager::getInstance(); //這里用了單例模式
//從slave數據庫讀取數據(目前實際上主從都是同一個,后續可以使用主從方式測試對比性能)
CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave"); //指定連接池
AUTO_REL_DBCONN(db_manager, db_conn); //退出當前函數時自動把連接歸還連接池
Redis連接池,目前設置了token,ranking_list兩個連接池,token用于token的讀寫,ranking_list用于下載 排行榜的操作,通過指定連接池獲取連接示例代碼:
CacheManager *cache_manager = CacheManager::getInstance();
CacheConn *cache_conn = cache_manager->GetCacheConn("token");//指定連接池
AUTO_REL_CACHECONN(cache_manager, cache_conn); //退出當前函數時自動把連接歸還連接池
2.3?實現完整的注冊功能
1. 部分頭文件以及通用函數 放到新建的api_common.h api_common.cc
2. 調用MySQL操作接口
3. 重點是api_register.cc的代碼完善
引用MySQL操作接口
#include "db_pool.h" // 相關的MySQL操作接口的頭文件
/*
* 先根據用戶名查詢數據庫該用戶是否存在,不存在才插入
*/
int registerUser(string &user_name, string &nick_name, string &pwd, string &phone, string &email) {int ret = 0;uint32_t user_id;CDBManager *db_manager = CDBManager::getInstance(); //這里用了單例模式//從slave數據庫讀取數據(目前實際上主從都是同一個,后續可以使用主從方式測試對比性能)CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);// 先查看用戶是否存在string str_sql = FormatString("select id, user_name from user_info where user_name='%s'", user_name.c_str());CResultSet *result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set && result_set->Next()) { // 檢測是否存在用戶記錄// 存在在返回LOG_WARN << "id: " << result_set->GetInt("id") << ", user_name: " << result_set->GetString("user_name")<< " 已經存在";delete result_set;ret = 2; // 說明用戶已經存在了} else { // 如果不存在則注冊time_t now;char create_time[TIME_STRING_LEN];//獲取當前時間now = time(NULL);strftime(create_time, TIME_STRING_LEN - 1, "%Y-%m-%d %H:%M:%S", localtime(&now));str_sql = "insert into user_info ""(`user_name`,`nick_name`,`password`,`phone`,`email`,`create_""time`) values(?,?,?,?,?,?)";LOG_INFO << "執行: " << str_sql;// 必須在釋放連接前delete// CPrepareStatement對象,否則有可能多個線程操作mysql對象,會crash// 預處理方式寫入數據CPrepareStatement *stmt = new CPrepareStatement();if (stmt->Init(db_conn->GetMysql(), str_sql)) {uint32_t index = 0;string c_time = create_time;stmt->SetParam(index++, user_name);stmt->SetParam(index++, nick_name);stmt->SetParam(index++, pwd);stmt->SetParam(index++, phone);stmt->SetParam(index++, email);stmt->SetParam(index++, c_time);bool bRet = stmt->ExecuteUpdate(); //真正提交要寫入的數據if (bRet) { //提交正常返回 trueret = 0;user_id = db_conn->GetInsertId();LOG_INFO << "insert user_id: " << user_id;} else {LOG_ERROR << "insert user_info failed. " << str_sql;ret = 1;}}delete stmt;}return ret;
}
2.4?實現完整的登錄功能
主要是完善:
- verifyUserPassword(string &user_name, string &pwd) 函數,通過用戶名查詢數據庫對應的密碼,對 比請求登錄的密碼是否一致
- setToken(string &user_name, string &token) 如果一致則 生成一個新token,并以token為key,用戶 名作為value,把token存儲到redis里。
2.4.1 校驗用戶名密碼verifyUserPassword
int verifyUserPassword(string &user_name, string &pwd) {int ret = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn); //析構時自動歸還連接// 根據用戶名查詢密碼string strSql = FormatString("select password from user_info where user_name='%s'", user_name.c_str());CResultSet *result_set = db_conn->ExecuteQuery(strSql.c_str());if (result_set && result_set->Next()) { //如果存在則讀取密碼// 存在在返回string password = result_set->GetString("password");LOG_INFO << "mysql-pwd: " << password << ", user-pwd: " << pwd;if (password == pwd) //對比密碼是否一致ret = 0; //對比成功elseret = -1; //對比失敗} else { // 說明用戶不存在ret = -1;}delete result_set;return ret;
}
2.4.2 使用redis實現token驗證機制
Redis是一個開源的內存數據存儲系統,可以用作數據庫、緩存和消息中間件。它支持多種數據結構,例如字 符串、哈希、列表、集合和有序集合。Redis通過將數據存儲在內存中,提供了非常高效的讀寫速度。
在Web應用程序中,使用Redis存儲Token有以下幾個優點:
- 快速:Redis的數據存儲在內存中,讀寫速度非常快,適用于高并發的場景。
- 可擴展性:Redis支持分布式部署,可以輕松實現橫向擴展。
- 多種數據結構:Redis支持多種數據結構,可以根據需求選擇適合的數據結構存儲Token。
- 過期時間管理:Redis提供了設置過期時間的功能,可以輕松實現Token的自動過期和續期。
因為我們使用token作為key,所以需要做到唯一,所以也就需要通過算法生成唯一的key,我們使用libuuid 這個庫。
Ubuntu安裝:
sudo apt-get install uuid-dev
使用方式:
頭文件引用 #include
CMakeLists.txt要:
包含路徑:INCLUDE_DIRECTORIES(/usr/include)
包含庫文件目錄:LINK_DIRECTORIES(/usr/lib)
包含庫的引用 uuid:TARGET_LINK_LIBRARIES(tc_http_srv3 .... uuid)
調用uuid的接口封裝generateUUID:
std::string generateUUID() {uuid_t uuid;uuid_generate_time_safe(uuid); //調用uuid的接口char uuidStr[40] = {0};uuid_unparse(uuid, uuidStr); //調用uuid的接口return std::string(uuidStr);
}
在redis設置token
int setToken(string &user_name, string &token) {int ret = 0;CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("token");AUTO_REL_CACHECONN(cache_manager, cache_conn);token = generateUUID(); // 生成唯一的tokenif (cache_conn) {//token - 用戶名, 86400有效時間為24小時 有效期可以自己修改cache_conn->SetEx(token, 86400, user_name); // redis做超時} else {ret = -1;}return ret;
}
校驗token和user_name
//驗證登陸token,成功返回0,失敗-1
int VerifyToken(string &user_name, string &token) {int ret = 0;CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("token");AUTO_REL_CACHECONN(cache_manager, cache_conn);if (cache_conn) {string temp_user_name = cache_conn->Get(token); //校驗token和用戶名的關系if (temp_user_name == user_name) {ret = 0;} else {ret = -1;}} else {ret = -1;}return ret;
}
參考連接:https://github.com/0voice