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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:44:56