C语言登录系统验证缺陷:仅首次尝试可成功验证求助
C语言登录系统验证问题修复
问题描述
我用C语言实现了一个登录系统,用户有3次输入正确user_id和password的机会,用完所有机会程序就退出。现在遇到的问题是:第一次登录失败后,后续就算输入正确的凭证,系统也没法通过验证,只有第一次尝试输入正确才能成功登录。
原实现代码
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char user_id[50]; char name[50]; char password[50]; char email[50]; char role[50]; } users; int main() { login_menu; return 0; } void login_menu() { printf("Login page"); FILE *users_file = fopen("users.txt", "r"); int authenticated = 0; int login_attempts = 0; const int max_login_attempts = 3; while (login_attempts < max_login_attempts) { users user; int valid_user_id = 0; char user_id[50]; char password[50]; printf("Please enter your User ID (TP number or Tutor code) down below: \n"); scanf("%s", user_id); printf("Please enter your password down below: \n"); scanf("%s", password); if ((user_id[0] == 'T' && user_id[1] == 'P') || (user_id[0] == 't' && user_id[1] == 'p')) { valid_user_id = 1; for (int i = 0; i < 50; i++) { user_id[i] = user_id[i + 2]; } } else if ((user_id[0] == 'T') || (user_id[0] == 't')) { valid_user_id = 1; for (int i = 0; i < 50; i++) { user_id[i] = user_id[i + 1]; } } else if ((user_id[0] == 'A') || (user_id[0] == 'a')) { valid_user_id = 1; for (int i = 0; i < 50; i++) { user_id[i] = user_id[i + 1]; } } if (valid_user_id) { // use while loop to read the file line by line while (fscanf(users_file, "%s %s %s %s %s \n", user.user_id, user.name, user.password, user.email, user.role) != EOF) { if (strcmp(user.user_id, user_id) == 0 && strcmp(user.password, password) == 0) { authenticated = 1; break; } } } if (authenticated) { fclose(users_file); printf("Login successful %s (%s) !\n", user.name, user.role); dashboard_menu(user); break; } else { login_attempts++; printf("Login id or password is incorrect, %d attempts left. Please try again.\n", (3 - login_attempts)); printf("Invalid c"); } } if (authenticated == 0) { printf("Max login attempts reached. Exiting program.\n"); } fclose(users_file); }
用户数据文件users.txt
988955 John 123456 admin@apu.edu.my admin 265663 Mary 123456 marry@apu.edu.my tutor 009650 Peter 123456 peter@apu.edu.my tutor 544654 James 123456 james@apu.edu.my tutor 577001 Johnny 123456 john@apu.edu.my tutor 683357 David 123456 david@apu.edu.my tutor
问题根源
核心问题是文件指针未重置:第一次尝试读取文件时,指针会移动到文件末尾,后续循环再调用fscanf时直接返回EOF,根本不会去匹配新输入的凭证。此外还有几个次要问题:
main函数中login_menu;是语法错误,应为函数调用login_menu();- 处理用户ID的循环会导致数组越界
authenticated标志未在每次尝试时重置- 文件关闭逻辑存在重复关闭或提前关闭的风险
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char user_id[50]; char name[50]; char password[50]; char email[50]; char role[50]; } users; // 提前声明函数,避免编译错误 void login_menu(); void dashboard_menu(users user); int main() { login_menu(); // 修正:正确调用函数 return 0; } void login_menu() { printf("Login page\n"); const int max_login_attempts = 3; int login_attempts = 0; while (login_attempts < max_login_attempts) { int authenticated = 0; // 每次尝试重置认证标志 users user; int valid_user_id = 0; char user_id[50]; char password[50]; char processed_user_id[50] = {0}; // 单独存储处理后的用户ID,避免修改原输入 printf("请输入您的用户ID(TP编号或导师代码):\n"); scanf("%s", user_id); printf("请输入您的密码:\n"); scanf("%s", password); // 安全处理用户ID前缀,用strncpy避免数组越界 if ((user_id[0] == 'T' && user_id[1] == 'P') || (user_id[0] == 't' && user_id[1] == 'p')) { valid_user_id = 1; strncpy(processed_user_id, user_id + 2, sizeof(processed_user_id) - 1); } else if ((user_id[0] == 'T' || user_id[0] == 't') || (user_id[0] == 'A' || user_id[0] == 'a')) { valid_user_id = 1; strncpy(processed_user_id, user_id + 1, sizeof(processed_user_id) - 1); } if (valid_user_id) { // 每次尝试重新打开文件,确保指针回到文件开头 FILE *users_file = fopen("users.txt", "r"); if (!users_file) { printf("无法打开用户文件!\n"); exit(1); } // 重新读取文件内容进行匹配 while (fscanf(users_file, "%s %s %s %s %s", user.user_id, user.name, user.password, user.email, user.role) != EOF) { if (strcmp(user.user_id, processed_user_id) == 0 && strcmp(user.password, password) == 0) { authenticated = 1; break; } } fclose(users_file); // 读取完成后立即关闭文件,避免资源泄漏 } if (authenticated) { printf("登录成功,欢迎 %s(%s)!\n", user.name, user.role); dashboard_menu(user); break; } else { login_attempts++; printf("用户名或密码错误,剩余尝试次数:%d,请重试。\n", max_login_attempts - login_attempts); } } if (login_attempts >= max_login_attempts) { printf("已达到最大登录尝试次数,程序退出。\n"); } } // 示例面板函数,可根据实际需求实现 void dashboard_menu(users user) { printf("进入用户面板...\n"); }
关键修改说明
- 每次尝试重新打开文件:彻底解决文件指针停留在末尾的问题,也可改用
fseek(users_file, 0, SEEK_SET)重置指针,但重新打开更直观 - 重置认证标志:将
authenticated移至循环内部,每次登录尝试都重新初始化 - 安全处理用户ID:用
strncpy替代数组循环,避免越界风险,同时单独存储处理后的ID - 修正语法错误:修复
main函数中的函数调用问题 - 完善文件操作:确保文件打开后必关闭,避免资源泄漏
- 清理无效输出:移除原代码中多余的错误输出语句
内容的提问来源于stack exchange,提问作者James Wong
相关产品推荐
相关产品推荐

