You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++登录系统开发求助:实现Hashing password algorithm及用户名冲突检测

分步解决你的C++登录系统问题

一、先搞定用户名冲突检查

要避免重复用户名,核心是注册前从存储文件读取所有已存在的用户名,和新输入的用户名对比。我们可以用文本文件(比如users.txt)存储用户信息,每行格式为用户名:加盐哈希密码。

实现步骤:

  1. 写一个辅助函数检查用户名是否已存在:
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;
}
  1. 在注册流程中调用该函数,若用户名已存在则提示用户更换:
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函数实现,步骤如下:

  1. 生成随机盐:
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;
}
  1. 加盐哈希密码:
#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();
}
  1. 验证密码:登录时取出存储的盐和哈希,用相同盐哈希输入密码后对比结果:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 05:10:56