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

如何为账户系统实现Discord式四位数字标签登录机制?

Got it, let's walk through implementing this Discord-style discriminator system for your user account setup—since you're working with MariaDB and the C++ REST SDK (Casablanca), I'll tailor every part to your tech stack:

1. Database Schema Design

First, you need a user table that enforces uniqueness for the username + discriminator pair, while allowing duplicate usernames. Here's a MariaDB schema that fits:

CREATE TABLE users (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    discriminator CHAR(4) NOT NULL, -- Stores the 4-digit tag (e.g., "1234")
    password_hash VARCHAR(255) NOT NULL, -- NEVER store plaintext passwords!
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    -- Critical unique constraint to prevent duplicate username+tag pairs
    UNIQUE KEY idx_username_discriminator (username, discriminator)
);

The idx_username_discriminator index ensures no two users share the exact same username and discriminator, while letting multiple users have the same username.

2. Discriminator Generation Logic

When a user signs up with a username, your system needs to generate a unique 4-digit tag for them. Here's how to implement this in C++ (assuming you're using the official MariaDB C++ connector):

#include <random>
#include <unordered_set>
#include <mysql/jdbc.h>

std::string generateUniqueDiscriminator(const std::string& username, sql::Connection* dbConn) {
    // Fetch all existing discriminators for the target username
    sql::PreparedStatement* stmt = dbConn->prepareStatement(
        "SELECT discriminator FROM users WHERE username = ?"
    );
    stmt->setString(1, username);
    sql::ResultSet* results = stmt->executeQuery();

    std::unordered_set<std::string> usedTags;
    while (results->next()) {
        usedTags.insert(results->getString("discriminator"));
    }

    // Clean up DB resources
    delete results;
    delete stmt;

    // Generate a random 4-digit tag (with leading zeros)
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dist(0, 9999);

    while (true) {
        int tagNum = dist(gen);
        char tagBuf[5];
        snprintf(tagBuf, sizeof(tagBuf), "%04d", tagNum); // Format as 4-digit string (e.g., 0012)
        std::string candidateTag(tagBuf);

        if (usedTags.find(candidateTag) == usedTags.end()) {
            return candidateTag;
        }

        // Handle edge case: all 10000 tags are taken for this username
        if (usedTags.size() >= 10000) {
            throw std::runtime_error("No available discriminators for this username—please choose a different one.");
        }
    }
}

This generates a random tag, checks if it's already in use for the username, and repeats until it finds an unused one.

3. User Creation REST Endpoint (Casablanca)

Integrate the discriminator logic into your Casablanca-based signup endpoint. Don't forget to hash passwords with a secure algorithm like bcrypt or Argon2 (never store plaintext!):

#include <cpprest/http_listener.h>
#include <cpprest/json.h>

using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;

// Assume you have a global/available DB connection pointer: sql::Connection* dbConn;
// Assume you have functions: std::string hashPassword(const std::string&); and uint32_t getLastInsertId(sql::Connection*);

void setupSignupEndpoint(http_listener& listener) {
    listener.support(methods::POST, "/api/auth/signup", [](http_request request) {
        request.extract_json().then([](json::value requestBody) {
            try {
                // Extract and validate input
                std::string username = requestBody.at(U("username")).as_string();
                std::string password = requestBody.at(U("password")).as_string();

                if (username.empty() || username.length() > 50 || password.empty()) {
                    throw std::invalid_argument("Invalid username or password.");
                }
                if (username.find('#') != std::string::npos) {
                    throw std::invalid_argument("Username cannot contain the '#' character.");
                }

                // Hash the password (use a battle-tested library like bcryptcpp)
                std::string passwordHash = hashPassword(password);

                // Generate unique discriminator
                std::string discriminator = generateUniqueDiscriminator(username, dbConn);

                // Insert user into DB
                sql::PreparedStatement* stmt = dbConn->prepareStatement(
                    "INSERT INTO users (username, discriminator, password_hash) VALUES (?, ?, ?)"
                );
                stmt->setString(1, username);
                stmt->setString(2, discriminator);
                stmt->setString(3, passwordHash);
                stmt->executeUpdate();

                uint32_t userId = getLastInsertId(dbConn);
                delete stmt;

                // Return success response
                json::value response;
                response[U("id")] = json::value::number(userId);
                response[U("username")] = json::value::string(username);
                response[U("discriminator")] = json::value::string(discriminator);

                request.reply(status_codes::OK, response);
            } catch (const std::exception& ex) {
                request.reply(status_codes::BadRequest, json::value::string(ex.what()));
            }
        });
    });
}
4. Login Validation Logic

When a user logs in with username#1234, split the input to validate against the correct user:

struct User {
    uint32_t id;
    std::string username;
    std::string discriminator;
};

// Assume you have a function: bool verifyPassword(const std::string& inputPass, const std::string& storedHash);

bool validateLogin(const std::string& loginIdentifier, const std::string& password, sql::Connection* dbConn, User& outUser) {
    // Split the identifier into username and discriminator
    size_t hashPos = loginIdentifier.find('#');
    if (hashPos == std::string::npos || hashPos == 0 || hashPos == loginIdentifier.length() - 1) {
        return false; // Invalid format (no #, or # at start/end)
    }

    std::string username = loginIdentifier.substr(0, hashPos);
    std::string discriminator = loginIdentifier.substr(hashPos + 1);

    // Validate discriminator is exactly 4 digits
    if (discriminator.length() != 4 || !std::all_of(discriminator.begin(), discriminator.end(), ::isdigit)) {
        return false;
    }

    // Fetch user from DB
    sql::PreparedStatement* stmt = dbConn->prepareStatement(
        "SELECT id, password_hash FROM users WHERE username = ? AND discriminator = ?"
    );
    stmt->setString(1, username);
    stmt->setString(2, discriminator);
    sql::ResultSet* results = stmt->executeQuery();

    if (results->next()) {
        std::string storedHash = results->getString("password_hash");
        outUser.id = results->getUInt("id");
        outUser.username = username;
        outUser.discriminator = discriminator;

        bool passwordValid = verifyPassword(password, storedHash);
        delete results;
        delete stmt;
        return passwordValid;
    }

    delete results;
    delete stmt;
    return false; // No matching user found
}
5. Edge Cases & Optimizations
  • Race Conditions: If two signup requests for the same username hit your API at the same time, they might generate the same discriminator. Fix this by catching MariaDB's duplicate key error (error code 1062) during insertion, then re-generating the discriminator and trying again.
  • Tag Generation Efficiency: For usernames with many existing users, random generation might cause collisions. You can alternatively query the highest existing discriminator, increment it, and wrap around to 0000 if you hit 9999 (though random is more aligned with Discord's behavior).
  • Index Performance: The idx_username_discriminator unique index will speed up login queries significantly, as it's used to look up users during validation.

内容的提问来源于stack exchange,提问作者Panakotta00

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:44:48