登录MySQL
这行命令告诉MySQL客户端程序用户root准备登录,-p表示告诉 MySQL 客户端程序提示输入密码。
mysql -u root -p
创建数据库
create database wifi;
use wifi;
create table password(user_password CHAR(8),primary key(user_password));
源码
代码编译
g++ wificreate.cpp -o wificreate -lmysqlclient
源码
#include <iostream>
#include <cstring>
#include <random>
#include <ctime>
#include <algorithm>
#include <mysql/mysql.h>std::string generatePassword() {const char* const numbers = "0123456789";const char* const upperchar = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";const char* const lowechar = "abcdefghijklmnopqrstuvwxyz";const char* const specials = "!@#$%^&*()-_=+[]{}|;:,.<>?";const int numChars = 8; // 密码长度std::string password;std::random_device rd;std::mt19937 generator(rd());std::uniform_int_distribution<> dis;// 初始化密码,确保包含至少一个数字、一个大写字母、一个小写字母和一个特殊字符password += numbers[dis(generator) % 10];password += upperchar[dis(generator) % 26];password += lowechar[dis(generator) % 26];password += specials[dis(generator) % 32];// 填充剩余的字符for (int i = 4; i < numChars; ++i) {std::string allChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()-_=+[]{}|;:,.<>?";dis = std::uniform_int_distribution<>(0, allChars.size() - 1);password += allChars[dis(generator)];}// 打乱密码中的字符顺序,以确保随机性std::shuffle(password.begin(), password.end(), generator);return password;
}bool isPasswordValid(const std::string& password) {if (password.length() != 8) {return false;}bool hasDigit = false;bool hasUpper = false;bool hasLower = false;bool hasSpecial = false;const std::string specials = "!@#$%^&*()-_=+[]{}|;:,.<>?";for (char ch : password) {if (isdigit(ch)) {hasDigit = true;} else if (isupper(ch)) {hasUpper = true;} else if (islower(ch)) {hasLower = true;} else if (specials.find(ch) != std::string::npos) {hasSpecial = true;}if (hasDigit && hasUpper && hasLower && hasSpecial) {return true;}}return hasDigit && hasUpper && hasLower && hasSpecial;
}bool insertOrUpdatePassword(MYSQL* conn, const std::string& password) {const char* insert_sql = "INSERT INTO password (user_password) VALUES (?) ON DUPLICATE KEY UPDATE user_password=VALUES(user_password)";MYSQL_STMT* stmt = mysql_stmt_init(conn);if (!stmt) {std::cerr << "stmt init failed" << std::endl;return false;}if (mysql_stmt_prepare(stmt, insert_sql, strlen(insert_sql))) {std::cerr << "prepare failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}MYSQL_BIND bind[1];memset(bind, 0, sizeof(bind));bind[0].buffer_type = MYSQL_TYPE_STRING;bind[0].buffer = (char*)password.c_str();bind[0].buffer_length = password.length();if (mysql_stmt_bind_param(stmt, bind)) {std::cerr << "bind failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}if (mysql_stmt_execute(stmt)) {std::cerr << "execute failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}mysql_stmt_close(stmt);return true;
}
bool clearTable(MYSQL* conn, const char* table_name) {// 创建一个足够长的缓冲区来存储完整的 SQL 语句char clear_sql[256];snprintf(clear_sql, sizeof(clear_sql), "DELETE FROM %s", table_name);MYSQL_STMT* stmt = mysql_stmt_init(conn);if (!stmt) {std::cerr << "stmt init failed" << std::endl;return false;}if (mysql_stmt_prepare(stmt, clear_sql, strlen(clear_sql))) {std::cerr << "prepare failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}if (mysql_stmt_execute(stmt)) {std::cerr << "execute failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}mysql_stmt_close(stmt);return true;
}int main(int argc,char* argv[]) {MYSQL connect;if (mysql_init(&connect) == NULL) {std::cerr << "mysql init err" << std::endl;exit(1);}if (!mysql_real_connect(&connect, "localhost", "root", "1", "wifi", 3306, NULL, 0)) {std::cerr << mysql_error(&connect) << std::endl;mysql_close(&connect);return 1;}std::string userPassWord = "";std::string rootPassWord = generatePassword();std::string initialPassword1 = rootPassWord;if (argc > 1) {if (strcmp(argv[1], "reset") == 0) {std::string initialPassword2 = initialPassword1; // 假设这是初始密码if (!clearTable(&connect, "password")) {std::cerr << "Failed to clear the password table." << std::endl;mysql_close(&connect);return 1;}if (!insertOrUpdatePassword(&connect, initialPassword2)) {std::cerr << "Failed to reset password." << std::endl;mysql_close(&connect);return 1;} else {std::cout << "Password has been reset to: " << initialPassword2 << std::endl;}mysql_close(&connect);return 0;}
}if (!insertOrUpdatePassword(&connect, rootPassWord)) {std::cerr << "Failed to insert or update password in database." << std::endl;mysql_close(&connect);return 1;}std::cout << "insert success" << std::endl;std::cout << "Generated Password :" << rootPassWord << std::endl;for (;;) {std::cout << "Please enter user password: ";std::cin >> userPassWord;if (isPasswordValid(userPassWord)) {if (!clearTable(&connect, "password")) {std::cerr << "Failed to clear table." << std::endl;} else {std::cout << "Table cleared successfully!" << std::endl;if (!insertOrUpdatePassword(&connect, userPassWord)) {std::cerr << "Failed to insert new password into database." << std::endl;} else {std::cout << "New password inserted successfully!" << std::endl;std::cout << "New rootPassword :" << userPassWord << std::endl;}}break;} else {std::cout << "Invalid password!!!" << std::endl;}}mysql_close(&connect);return 0;
}
清除函数
bool clearTable(MYSQL* conn, const char* table_name) {// 创建一个足够长的缓冲区来存储完整的 SQL 语句char clear_sql[256];snprintf(clear_sql, sizeof(clear_sql), "DELETE FROM %s", table_name);MYSQL_STMT* stmt = mysql_stmt_init(conn);if (!stmt) {std::cerr << "stmt init failed" << std::endl;return false;}if (mysql_stmt_prepare(stmt, clear_sql, strlen(clear_sql))) {std::cerr << "prepare failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}if (mysql_stmt_execute(stmt)) {std::cerr << "execute failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}mysql_stmt_close(stmt);return true;
}
snprintf()
函数原型
int snprintf(char *str, size_t size, const char *format, ...);
snprintf 是一个标准 C 库函数,用于将格式化的数据写入字符串中。它比 sprintf 更安全,因为它可以指定目标缓冲区的最大大小,从而避免缓冲区溢出的问题。
snprintf(clear_sql, sizeof(clear_sql), "DELETE FROM %s", table_name);
函数参数
char *str
clear_sql:这是目标缓冲区,snprintf 将把格式化后的字符串写入这个变量。它应该是一个字符数组,足够大以存储最终的 SQL 语句。
size_t size
sizeof(clear_sql):这是目标缓冲区的大小。sizeof 运算符用于确定
clear_sql 数组的大小,以确保写入的数据不会超出数组的界限。
const char *format
“DELETE FROM %s”:这是格式化字符串,它包含了要构造的 SQL 语句的模板。%s 是一个占位符,用于插入一个字符串参数。
…
table_name:这是一个字符串变量,包含了要删除记录的表名。它将替换格式化字符串中的 %s 占位符。
函数功能
行代码的功能是生成一个 DELETE 语句,用于删除指定表 table_name 中的所有记录。例如,如果 table_name 是 “users”,那么生成的 SQL 语句将是 “DELETE FROM users”。
注意事项:
需要确保 table_name 是一个有效的表名,且不包含任何 SQL 注入攻击的风险。在实际应用中,如果 table_name 来自于用户输入,应该进行严格的验证和清理,以防止潜在的安全问题。
snprintf 会在字符串的末尾确保添加空字符(‘\0’),以形成一个正确的 C 字符串。
如果格式化后的字符串长度超过了 clear_sql 缓冲区的大小,snprintf 会根据缓冲区的大小截断字符串,以防止溢出。
MYSQL_STMT结构体
MYSQL_STMT* stmt = mysql_stmt_init(conn);
这行代码是在使用 MySQL C API 时,初始化一个语句对象的步骤。下面是详细解释:
MYSQL_STMT* stmt:
这是一个指向 MYSQL_STMT 结构的指针,MYSQL_STMT 是 MySQL C API 中用于表示预编译 SQL 语句的结构体。
mysql_stmt_init(conn):
mysql_stmt_init 函数用于初始化一个 MYSQL_STMT 结构体,为后续的预编译 SQL 语句做准备。
conn 参数是一个指向 M YSQL 结构的指针,它代表了一个与 MySQL 数据库的连接。这个连接必须在此之前已经成功建立。
功能:
当你调用 mysql_stmt_init(conn) 时,它会为指定的数据库连接 conn 创建一个新的语句对象。
如果初始化成功,函数会返回一个指向新初始化的 MYSQL_STMT 结构的指针,你可以使用这个指针来执行后续的 SQL 语句预编译、参数绑定、执行等操作。
如果初始化失败,函数会返回 NULL。
mysql_stmt_close
在使用完 MYSQL_STMT 对象后,应该调用 mysql_stmt_close 函数来释放与之相关的资源。
在调用 mysql_stmt_init 之前,必须确保 conn 指向的 MYSQL 结构已经通过 mysql_real_connect 或类似的函数成功连接到数据库。
mysql_stmt_init 只是初始化了一个语句对象,你需要使用 mysql_stmt_prepare 函数来准备(预编译)一个 SQL 语句,然后才能执行它。
mysql_stmt_prepare
mysql_stmt_prepare(stmt, clear_sql, strlen(clear_sql))
这行代码是在使用 MySQL C API 时,准备(预编译)一个 SQL 语句的步骤。下面是详细解释:
mysql_stmt_prepare 函数用于预编译一个 SQL 语句,这样可以提高性能,并且可以防止 SQL 注入攻击。
参数解释:
这个函数需要三个参数:一个 MYSQL_STMT 指针,指向要准备的 SQL 语句的字符串,以及 SQL 语句字符串的长度。
stmt
这是一个指向 MYSQL_STMT 结构的指针,它是一个语句对象,之前已经通过 mysql_stmt_init 函数初始化。
clear_sql
这是一个包含要预编译的 SQL 语句的字符串。
strlen(clear_sql)
这是 clear_sql 字符串的长度。使用 strlen 函数来获取长度是为了确保在预编译过程中,MySQL 知道字符串的确切结束位置。
功能:
当你调用 mysql_stmt_prepare(stmt, clear_sql, strlen(clear_sql)) 时,MySQL 会检查 clear_sql 字符串中的 SQL 语句,并准备执行它。
预编译的语句可以包含参数占位符(如 ?),这些占位符在后续的 mysql_stmt_bind_param 调用中会被具体的参数值替换。
返回值:
如果预编译成功,mysql_stmt_prepare 函数返回 0。
如果预编译失败,函数返回非零值,你可以通过 mysql_stmt_error 函数获取错误信息。
注意事项:
在调用 mysql_stmt_prepare 之前,必须确保 stmt 已经通过 mysql_stmt_init 函数初始化。
如果预编译失败,应该检查错误信息,并根据错误信息进行相应的处理。
预编译的语句在使用完成后应该通过 mysql_stmt_close 函数关闭,以释放资源。
预编译语句是数据库操作中的一个重要步骤,它可以提高数据库操作的安全性和效率。通过预编译,数据库服务器可以重用编译后的语句,而不需要每次都解析和编译 SQL 语句。
mysql_stmt_close(stmt)
mysql_stmt_close(stmt) 是 MySQL C API 中用来关闭语句对象的函数调用。下面是对这个函数调用的详细解释:
函数定义:
mysql_stmt_close 是一个函数,用于关闭之前通过 mysql_stmt_init 创建的 MYSQL_STMT 语句对象。
参数:
stmt
这是一个指向 MYSQL_STMT 结构的指针,代表要关闭的语句对象。
功能:
当你完成一个语句对象的所有数据库操作后(例如,预编译 SQL 语句、绑定参数、执行语句等),你应该调用 mysql_stmt_close 来释放与该语句对象关联的所有资源。
这个函数会关闭语句对象,并且释放内存和其他系统资源,确保不会有内存泄漏。
返回值:
mysql_stmt_close 函数没有返回值(它的返回类型是 void)。
注意事项:
在关闭语句对象之前,确保已经完成了所有需要的数据库操作。
如果 stmt 是 NULL,mysql_stmt_close 函数不会有任何效果,因为没有任何资源需要关闭。
在关闭语句对象后,不应该再使用该对象进行任何操作,因为它已经不再有效。
关闭语句对象是一个良好的编程实践,它有助于维护资源的有效管理,并确保应用程序的稳定性和性能。在编写使用 MySQL C API 的应用程序时,应该在不再需要语句对象时及时关闭它们。
mysql_stmt_execute(stmt)
mysql_stmt_execute(stmt) 是 MySQL C API 中用于执行预编译 SQL 语句的函数。下面是对这个函数调用的详细解释:
函数定义:
mysql_stmt_execute 函数用于执行一个已经通过 mysql_stmt_prepare 预编译的 MYSQL_STMT 语句对象。
参数:
stmt:这是一个指向 MYSQL_STMT 结构的指针,代表要执行的预编译语句对象。
功能:
当你调用 mysql_stmt_execute(stmt) 时,MySQL 会执行与 stmt 相关联的预编译 SQL 语句。
这个函数可以执行 SELECT、INSERT、UPDATE、DELETE 等类型的 SQL 语句,具体取决于预编译时提供的 SQL 语句。
返回值:
如果执行成功,mysql_stmt_execute 函数返回 0。
如果执行失败,函数返回非零值,你可以通过 mysql_error 函数获取错误信息,通过 mysql_stmt_errno 函数获取错误代码。
注意事项:
在调用 mysql_stmt_execute 之前,必须确保 stmt 已经通过 mysql_stmt_prepare 预编译。
如果预编译的 SQL 语句包含参数占位符(如 ?),则需要先通过 mysql_stmt_bind_param 函数绑定实际的参数值。
对于 SELECT 语句,执行后可能需要调用 mysql_stmt_store_result 来存储结果集,然后通过 mysql_stmt_fetch 函数来检索结果集中的行。
对于 INSERT、UPDATE 或 DELETE 等不返回结果集的语句,通常不需要存储结果集,但可以通过 mysql_stmt_affected_rows 函数获取受影响的行数。
插入函数
bool insertOrUpdatePassword(MYSQL* conn, const std::string& password) {const char* insert_sql = "INSERT INTO password (user_password) VALUES (?) ON DUPLICATE KEY UPDATE user_password=VALUES(user_password)";MYSQL_STMT* stmt = mysql_stmt_init(conn);if (!stmt) {std::cerr << "stmt init failed" << std::endl;return false;}if (mysql_stmt_prepare(stmt, insert_sql, strlen(insert_sql))) {std::cerr << "prepare failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}MYSQL_BIND bind[1];memset(bind, 0, sizeof(bind));bind[0].buffer_type = MYSQL_TYPE_STRING;bind[0].buffer = (char*)password.c_str();bind[0].buffer_length = password.length();if (mysql_stmt_bind_param(stmt, bind)) {std::cerr << "bind failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}if (mysql_stmt_execute(stmt)) {std::cerr << "execute failed: " << mysql_error(conn) << std::endl;mysql_stmt_close(stmt);return false;}mysql_stmt_close(stmt);return true;
}
mysql_stmt_bind_param(stmt, bind)
mysql_stmt_bind_param(stmt, bind) 是 MySQL C API 中用于将应用程序变量绑定到预编译 SQL 语句的参数占位符的函数。下面是对这个函数调用的详细解释:
函数定义:
mysql_stmt_bind_param 函数用于在执行预编译的 SQL 语句之前,将应用程序中的数据绑定到 SQL 语句中的参数位置上。
参数:
stmt
这是一个指向 MYSQL_STMT 结构的指针,代表已经预编译的 SQL 语句对象。
bind
这是一个指向 MYSQL_BIND 结构数组的指针,每个 MYSQL_BIND 结构定义了一个参数的绑定信息,包括参数的数据类型、指向数据的指针、长度等。
功能:
当你的 SQL 语句中包含参数占位符(如 ?),你可以使用 mysql_stmt_bind_param 来指定这些占位符应该被哪些实际的值替换。
这个函数允许你将 C 语言中的变量或数据结构绑定到 SQL 语句中的参数上,这样当 SQL 语句执行时,MySQL 服务器会使用这些绑定的值。
返回值:
如果绑定成功,mysql_stmt_bind_param 函数返回 0。
如果绑定失败,函数返回非零值,你可以通过 mysql_error 函数获取错误信息,通过 mysql_stmt_errno 函数获取错误代码。
注意事项:
在调用 mysql_stmt_bind_param 之前,必须确保 stmt 已经通过 mysql_stmt_prepare 预编译,并且 SQL 语句中包含了参数占位符。
bind 数组中的每个 MYSQL_BIND 结构必须正确初始化,包括设置 buffer_type、buffer、buffer_length 等字段。
对于输入参数和输出参数(例如,用于存储 SELECT 语句返回值的参数),需要分别设置 MYSQL_BIND 结构的相应字段。