一、整体架构分析
本模块采用典型的C/S架构设计,客户端使用Qt框架实现GUI界面和本地数据交互功能。代码结构包含5个核心文件:
- main.cpp:应用程序入口
- mainwindow.h/cpp:主窗口逻辑实现
- registerdialog.h/cpp:注册对话框实现
- (隐含)ui_mainwindow.h:由Qt Designer生成的界面布局文件
模块功能包含:
- 用户登录验证
- 新用户注册
- 本地用户数据存储(JSON格式)
- 密码安全处理
- 交互反馈机制
二、核心模块详解
1. 应用程序入口(main.cpp)
cpp
Copy
#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
- 初始化Qt应用程序对象
- 创建MainWindow实例并显示
- 进入主事件循环(a.exec())
- 典型Qt应用程序启动模式,负责程序生命周期管理
2. 主窗口模块(MainWindow)
2.1 类声明(mainwindow.h)
cpp
Copy
#include <QMainWindow>
#include <QJsonDocument>
#include "registerdialog.h"class MainWindow : public QMainWindow
{Q_OBJECT
public:// 构造/析构函数MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:// 槽函数声明void onLoginClicked();void onRegisterClicked();private:Ui::MainWindow *ui; // UI组件指针QJsonArray readUserData(); // 数据读取bool validateUser(const QString &username, const QString &password); // 验证逻辑
};
- 继承QMainWindow实现主窗口
- 使用Qt的元对象系统(Q_OBJECT宏)
- 封装用户数据操作和验证逻辑
- 声明两个核心槽函数响应按钮操作
2.2 功能实现(mainwindow.cpp)
2.2.1 构造函数
cpp
Copy
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 密码框显示模式设置ui->lineEdit_2->setEchoMode(QLineEdit::Password);// 信号槽连接connect(ui->pushButton_2, &QPushButton::clicked, this, &MainWindow::onLoginClicked);connect(ui->pushButton_3, &QPushButton::clicked,this, &MainWindow::onRegisterClicked);
}
- 初始化UI组件(通过ui_mainwindow.h自动生成的代码)
- 设置密码输入框为密文显示
- 连接按钮点击信号到对应槽函数
2.2.2 数据读取
cpp
Copy
QJsonArray MainWindow::readUserData()
{QFile file("userdata.json");if(!file.open(QIODevice::ReadOnly)) {return QJsonArray();}QByteArray data = file.readAll();file.close();QJsonDocument doc = QJsonDocument::fromJson(data);return doc.array();
}
- 使用QFile读取本地JSON文件
- 异常处理:文件打开失败时返回空数组
- 通过QJsonDocument解析数据
- 返回用户数据数组,为后续验证提供数据源
2.2.3 登录验证
cpp
Copy
bool MainWindow::validateUser(const QString &username, const QString &password)
{QJsonArray users = readUserData();foreach(const QJsonValue &value, users){QJsonObject obj = value.toObject();if(obj["username"] == username && obj["password"] == password){return true;}}return false;
}
- 遍历JSON数组进行用户匹配
- 当前实现为明文验证(存在安全隐患)
- 典型改进方向:密码哈希存储、加盐处理
2.2.4 登录事件处理
cpp
Copy
void MainWindow::onLoginClicked()
{QString username = ui->lineEdit->text();QString password = ui->lineEdit_2->text();if(validateUser(username, password)){QMessageBox::information(this, "成功", "登录成功!");}else{QMessageBox::warning(this, "错误", "用户名或密码错误");}
}
- 获取界面输入值
- 调用验证函数进行凭证核对
- 使用QMessageBox提供交互反馈
- 当前为本地验证,C/S架构中应改为向服务器发送验证请求
3. 注册对话框模块(RegisterDialog)
3.1 类声明(registerdialog.h)
cpp
Copy
class RegisterDialog : public QDialog
{Q_OBJECT
public:explicit RegisterDialog(QWidget *parent = nullptr);private slots:void onRegisterSubmit();private:// UI组件QLineEdit *usernameEdit;QLineEdit *passwordEdit;QPushButton *submitButton;// 数据操作QJsonArray readUsers();void writeUsers(const QJsonArray &users);
};
- 继承QDialog实现模态对话框
- 封装用户注册数据操作
- 声明界面组件指针和数据处理方法
3.2 功能实现(registerdialog.cpp)
3.2.1 构造函数
cpp
Copy
RegisterDialog::RegisterDialog(QWidget *parent) : QDialog(parent)
{setWindowTitle("注册");resize(300, 200);// 用户名输入框usernameEdit = new QLineEdit(this);usernameEdit->setPlaceholderText("用户名");usernameEdit->setGeometry(50, 30, 200, 25);// 密码输入框passwordEdit = new QLineEdit(this);passwordEdit->setPlaceholderText("密码");passwordEdit->setEchoMode(QLineEdit::Password);passwordEdit->setGeometry(50, 70, 200, 25);// 提交按钮submitButton = new QPushButton("提交", this);submitButton->setGeometry(100, 120, 100, 30);connect(submitButton, &QPushButton::clicked,this, &RegisterDialog::onRegisterSubmit);
}
- 手工布局UI组件(替代方案:使用Qt Designer生成)
- 设置输入框占位符提示文字
- 密码框设置为密文显示
- 连接按钮点击信号到提交槽函数
3.2.2 数据写入
cpp
Copy
void RegisterDialog::writeUsers(const QJsonArray &users)
{QFile file("userdata.json");if(file.open(QIODevice::WriteOnly)){QJsonDocument doc(users);file.write(doc.toJson());file.close();}
}
- 将JSON数据写入本地文件
- 使用QJsonDocument序列化数据
- 需要注意的并发问题:多客户端同时写入时需要加锁
3.2.3 注册提交处理
cpp
Copy
void RegisterDialog::onRegisterSubmit()
{QString username = usernameEdit->text();QString password = passwordEdit->text();if(username.isEmpty() || password.isEmpty()){QMessageBox::warning(this, "错误", "请输入完整信息");return;}QJsonArray users = readUsers();// 检查用户是否存在foreach(const QJsonValue &value, users){QJsonObject obj = value.toObject();if(obj["username"] == username){QMessageBox::warning(this, "错误", "用户名已存在");return;}}// 添加新用户QJsonObject newUser;newUser["username"] = username;newUser["password"] = password;users.append(newUser);writeUsers(users);QMessageBox::information(this, "成功", "注册成功");this->close();
}
- 执行输入有效性检查
- 检测用户名冲突
- 构造新用户JSON对象
- 持久化存储数据
- 提供操作反馈后关闭对话框
三、数据存储设计
- 存储格式:JSON文件
json
Copy
[{"username": "user1", "password": "123456"},{"username": "user2", "password": "abcdef"}
]
- 存储路径:应用程序运行目录下的userdata.json
- 读写方式:
- 读取:QFile::ReadOnly + QJsonDocument解析
- 写入:QFile::WriteOnly + JSON序列化
- 安全性:
- 当前为明文存储密码(需改进)
- 无数据加密措施
- 无写入权限验证
四、界面设计分析
- 主窗口布局:
- 用户名输入框(lineEdit)
- 密码输入框(lineEdit_2)
- 登录按钮(pushButton_2)
- 注册按钮(pushButton_3)
- 注册对话框:
- 模态对话框设计
- 独立输入界面
- 提交后自动关闭
- 交互反馈:
- 使用QMessageBox的多种提示类型
- 信息提示(information)
- 错误警告(warning)
五、安全机制分析
- 密码显示:
- 设置setEchoMode(QLineEdit::Password)
- 防止输入时被旁观者窥视
- 输入验证:
- 非空检查
- 用户名唯一性校验
- 待改进项:
- 密码应进行哈希处理(如SHA-256)
- 增加验证码机制
- 实现SSL加密通信(向服务器传输时)
- 防止SQL注入(当前JSON存储不涉及)
六、通信机制扩展建议
当前实现为本地单机版本,按C/S架构要求可扩展以下功能:
-
网络通信层:
cpp
Copy
// 伪代码示例 void MainWindow::sendLoginRequest(QString user, QString pass) {QNetworkRequest request(QUrl("https://api.example.com/login"));request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");QJsonObject params;params["username"] = user;params["password"] = pass;QNetworkReply *reply = manager->post(request, QJsonDocument(params).toJson());connect(reply, &QNetworkReply::finished, [=](){// 处理服务器响应}); }
-
增加通信状态管理
-
实现会话保持(Cookie/JWT Token)
-
支持HTTPS安全传输
完整代码
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTcpSocket>
#include <QLineEdit>
#include <QPushButton>
#include <QMessageBox>
#include <QFile>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include "registerdialog.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();
private:QTcpSocket *tcpSocket;void handleServerResponse();
private slots:void onLoginClicked();void onRegisterClicked();void on_checkBox_checkStateChanged(const Qt::CheckState &arg1);void on_checkBox_stateChanged(int state);
private:Ui::MainWindow *ui;QJsonArray readUserData();bool validateUser(const QString &username, const QString &password);
};
#endif // MAINWINDOW_H
RegisterDialog.h
#ifndef REGISTERDIALOG_H
#define REGISTERDIALOG_H#include <QDialog>
#include <QLineEdit>
#include <QPushButton>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QFile>
#include <QMessageBox>class RegisterDialog : public QDialog
{Q_OBJECTpublic:explicit RegisterDialog(QWidget *parent = nullptr);private slots:void onRegisterSubmit();private:QLineEdit *usernameEdit;QLineEdit *passwordEdit;QPushButton *submitButton;QJsonArray readUsers();void writeUsers(const QJsonArray &users);
};
#endif // REGISTERDIALOG_H
successwindow.h
#ifndef SUCCESSWINDOW_H
#define SUCCESSWINDOW_H#include <QWidget>
#include <QLabel>class SuccessWindow : public QWidget
{Q_OBJECT
public:explicit SuccessWindow(QWidget *parent = nullptr);private:QLabel *welcomeLabel;
};#endif // SUCCESSWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "successwindow.h" // 添加这行头文件包含
// mainwindow.cpp 构造函数修改
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), tcpSocket(new QTcpSocket(this))
{ui->setupUi(this);// 设置密码输入框的显示模式ui->lineEdit_2->setEchoMode(QLineEdit::Password);// 连接信号与槽connect(tcpSocket, &QTcpSocket::readyRead, this, &MainWindow::handleServerResponse);connect(ui->pushButton_2, &QPushButton::clicked, this, &MainWindow::onLoginClicked);connect(ui->pushButton_3, &QPushButton::clicked, this, &MainWindow::onRegisterClicked);
}MainWindow::~MainWindow()
{delete ui;
}QJsonArray MainWindow::readUserData()
{QFile file("userdata.json");if(!file.open(QIODevice::ReadOnly)) {return QJsonArray();}QByteArray data = file.readAll();file.close();return QJsonDocument::fromJson(data).array();
}bool MainWindow::validateUser(const QString &username, const QString &password)
{QJsonArray users = readUserData();for (const QJsonValue &userValue : users) {QJsonObject user = userValue.toObject();QString storedUsername = user["username"].toString();QString storedPassword = user["password"].toString();if (storedUsername == username && storedPassword == password) {return true;}}return false;
}
void MainWindow::onLoginClicked() {QString username = ui->lineEdit->text();QString password = ui->lineEdit_2->text();QTcpSocket *socket = new QTcpSocket(this);connect(socket, &QTcpSocket::connected, [=]() {QJsonObject request;request["action"] = "login";request["username"] = username;request["password"] = password;socket->write(QJsonDocument(request).toJson());});connect(socket, &QTcpSocket::readyRead, [=]() {QJsonDocument doc = QJsonDocument::fromJson(socket->readAll());bool success = doc.object()["success"].toBool();if (success) {QMessageBox::information(this, "登录成功", "欢迎!");} else {QMessageBox::warning(this, "登录失败", "用户名或密码错误!");}socket->disconnectFromHost();});socket->connectToHost("127.0.0.1", 12345); // 连接服务端IP和端口
}
// mainwindow.cpp
void MainWindow::handleServerResponse() // 注意类作用域前缀
{QJsonDocument doc = QJsonDocument::fromJson(tcpSocket->readAll());QJsonObject response = doc.object();if(response["status"].toString() == "success") {SuccessWindow *successWindow = new SuccessWindow();successWindow->show();this->hide();} else {QMessageBox::warning(this, "错误", "登录失败: " + response["message"].toString());}
}void MainWindow::onRegisterClicked()
{RegisterDialog regDialog;regDialog.exec();
}void MainWindow::on_checkBox_stateChanged(int state)
{// 获取密码输入框(假设对象名为lineEdit_2)QLineEdit *passwordEdit = ui->lineEdit_2;// 根据复选框状态切换显示模式if (state == Qt::Checked) {passwordEdit->setEchoMode(QLineEdit::Normal); // 显示明文} else {passwordEdit->setEchoMode(QLineEdit::Password); // 隐藏密码}
}
registerdialog.cpp
#include "registerdialog.h"RegisterDialog::RegisterDialog(QWidget *parent) : QDialog(parent)
{setWindowTitle("注册");resize(300, 200);usernameEdit = new QLineEdit(this);usernameEdit->setPlaceholderText("用户名");usernameEdit->setGeometry(50, 30, 200, 25);passwordEdit = new QLineEdit(this);passwordEdit->setPlaceholderText("密码");passwordEdit->setEchoMode(QLineEdit::Password);passwordEdit->setGeometry(50, 70, 200, 25);submitButton = new QPushButton("提交", this);submitButton->setGeometry(100, 120, 100, 30);connect(submitButton, &QPushButton::clicked, this, &RegisterDialog::onRegisterSubmit);
}QJsonArray RegisterDialog::readUsers()
{QFile file("userdata.json");if(!file.open(QIODevice::ReadOnly)) {return QJsonArray();}QByteArray data = file.readAll();file.close();return QJsonDocument::fromJson(data).array();
}void RegisterDialog::writeUsers(const QJsonArray &users)
{QFile file("userdata.json");if(file.open(QIODevice::WriteOnly)){QJsonDocument doc(users);file.write(doc.toJson());file.close();}
}void RegisterDialog::onRegisterSubmit()
{QString username = usernameEdit->text();QString password = passwordEdit->text();if(username.isEmpty() || password.isEmpty()){QMessageBox::warning(this, "错误", "用户名和密码不能为空!");return;}QJsonArray users = readUsers();for(const QJsonValue &user : users){if(user.toObject()["username"] == username){QMessageBox::warning(this, "错误", "用户名已存在!");return;}}QJsonObject newUser;newUser["username"] = username;newUser["password"] = password;users.append(newUser);writeUsers(users);QMessageBox::information(this, "成功", "注册成功!");this->close();
}
successwindow.cpp
#include "successwindow.h"SuccessWindow::SuccessWindow(QWidget *parent): QWidget(parent)
{setWindowTitle("登录成功");resize(400, 300);welcomeLabel = new QLabel("欢迎使用系统!", this);welcomeLabel->setAlignment(Qt::AlignCenter);welcomeLabel->setGeometry(0, 0, 400, 300);
}
main.cpp
#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}