C++登录系统开发求助:实现Hashing password algorithm及用户名冲突检测
分步解决你的C++登录系统问题
一、先搞定用户名冲突检查
要避免重复用户名,核心是注册前从存储文件读取所有已存在的用户名,和新输入的用户名对比。我们可以用文本文件(比如users.txt)存储用户信息,每行格式为用户名:加盐哈希密码。
实现步骤:
- 写一个辅助函数检查用户名是否已存在:
bool isUsernameExists(const string& username) { ifstream file("users.txt"); string line; while (getline(file, line)) { size_t colonPos = line.find(':'); if (colonPos != string::npos) { string existingUser = line.substr(0, colonPos); if (existingUser == username) { return true; } } } return false; }
- 在注册流程中调用该函数,若用户名已存在则提示用户更换:
void signUpAttempt() { string username, password; cout << "Pick a Username\nUsername: "; while (cin >> username) { if (isUsernameExists(username)) { cout << "Username already taken! Pick another one: "; } else { break; } } cout << "Pick a Password\nPassword: "; cin >> password; saveUserToFile(username, hashPasswordWithSalt(password)); }
二、密码加密存储:加盐哈希
直接哈希密码易被彩虹表破解,必须加盐——给每个密码添加随机字符串后再哈希,最终将盐与哈希值一起存储。
新手友好的SHA-256加盐实现
我们借助OpenSSL库的SHA-256函数实现,步骤如下:
- 生成随机盐:
string generateSalt(int length = 16) { const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; string salt; srand(time(nullptr)); for (int i = 0; i < length; ++i) { salt += charset[rand() % (sizeof(charset) - 1)]; } return salt; }
- 加盐哈希密码:
#include <openssl/sha.h> #include <sstream> #include <iomanip> string hashPasswordWithSalt(const string& password) { string salt = generateSalt(); string saltedPassword = password + salt; unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(reinterpret_cast<const unsigned char*>(saltedPassword.c_str()), saltedPassword.size(), hash); stringstream ss; ss << salt << ":"; for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) { ss << hex << setw(2) << setfill('0') << static_cast<int>(hash[i]); } return ss.str(); }
- 验证密码:登录时取出存储的盐和哈希,用相同盐哈希输入密码后对比结果:
bool verifyPassword(const string& inputPassword, const string& storedHash) { size_t colonPos = storedHash.find(':'); if (colonPos == string::npos) return false; string salt = storedHash.substr(0, colonPos); string targetHash = storedHash.substr(colonPos + 1); string saltedPassword = inputPassword + salt; unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(reinterpret_cast<const unsigned char*>(saltedPassword.c_str()), saltedPassword.size(), hash); stringstream ss; for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) { ss << hex << setw(2) << setfill('0') << static_cast<int>(hash[i]); } return ss.str() == targetHash; }
三、整合完整代码
修正原有逻辑错误(比如把“已有账号”分支改为登录流程),整合所有功能:
#include <iostream> #include <fstream> #include <string> #include <algorithm> #include <openssl/sha.h> #include <sstream> #include <iomanip> #include <cstdlib> #include <ctime> using namespace std; bool isUsernameExists(const string& username) { ifstream file("users.txt"); string line; while (getline(file, line)) { size_t colonPos = line.find(':'); if (colonPos != string::npos) { string existingUser = line.substr(0, colonPos); if (existingUser == username) { return true; } } } return false; } string generateSalt(int length = 16) { const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; string salt; srand(time(nullptr)); for (int i = 0; i < length; ++i) { salt += charset[rand() % (sizeof(charset) - 1)]; } return salt; } string hashPasswordWithSalt(const string& password) { string salt = generateSalt(); string saltedPassword = password + salt; unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(reinterpret_cast<const unsigned char*>(saltedPassword.c_str()), saltedPassword.size(), hash); stringstream ss; ss << salt << ":"; for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) { ss << hex << setw(2) << setfill('0') << static_cast<int>(hash[i]); } return ss.str(); } void saveUserToFile(const string& username, const string& hashedPassword) { ofstream file("users.txt", ios::app); if (file.is_open()) { file << username << ":" << hashedPassword << endl; file.close(); cout << "Sign up successful!\n"; } else { cout << "Error saving user data!\n"; } } bool verifyPassword(const string& inputPassword, const string& storedHash) { size_t colonPos = storedHash.find(':'); if (colonPos == string::npos) return false; string salt = storedHash.substr(0, colonPos); string targetHash = storedHash.substr(colonPos + 1); string saltedPassword = inputPassword + salt; unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256(reinterpret_cast<const unsigned char*>(saltedPassword.c_str()), saltedPassword.size(), hash); stringstream ss; for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) { ss << hex << setw(2) << setfill('0') << static_cast<int>(hash[i]); } return ss.str() == targetHash; } void signUp() { string username, password; cout << "=== Sign Up ===\n"; cout << "Enter Username: "; while (cin >> username) { if (isUsernameExists(username)) { cout << "Username already taken! Try another: "; } else { break; } } cout << "Enter Password: "; cin >> password; string hashedPwd = hashPasswordWithSalt(password); saveUserToFile(username, hashedPwd); } bool login() { string username, password; cout << "=== Login ===\n"; cout << "Enter Username: "; cin >> username; ifstream file("users.txt"); string line; string storedHash; bool userFound = false; while (getline(file, line)) { size_t colonPos = line.find(':'); if (colonPos != string::npos) { string existingUser = line.substr(0, colonPos); if (existingUser == username) { storedHash = line.substr(colonPos + 1); userFound = true; break; } } } if (!userFound) { cout << "Username not found!\n"; return false; } cout << "Enter Password: "; cin >> password; if (verifyPassword(password, storedHash)) { cout << "Login successful!\n"; return true; } else { cout << "Wrong password!\n"; return false; } } int main() { string choice; while (true) { cout << "Have you played before? (y/n, q to quit): "; getline(cin >> ws, choice); transform(choice.begin(), choice.end(), choice.begin(), ::tolower); if (choice == "y" || choice == "yes") { if (login()) { break; } } else if (choice == "n" || choice == "no") { signUp(); } else if (choice == "q") { cout << "Exiting...\n"; break; } else { cout << "Invalid input! Please enter y/n/q.\n"; } } return 0; }
编译与运行注意事项
- GCC编译时需链接OpenSSL库:
g++ login_system.cpp -o login_system -lcrypto - 首次运行会自动创建
users.txt存储用户信息
关键知识点说明
- 加盐哈希:每个密码对应唯一盐,避免相同密码产生相同哈希值,抵御彩虹表攻击
- 文件存储:文本文件适合新手理解,个人项目足够使用,实际项目可替换为数据库
- 逻辑修正:调整原有流程,将“已有账号”对应登录、“新用户”对应注册,符合常规交互逻辑
内容的提问来源于stack exchange,提问作者Thomas Lindley
相关产品推荐
相关产品推荐

