处理 RSA 加密和解密的工具类 :SslTool
SslTool
类用于执行 RSA 加密和解密操作。- 包含 RSA 公钥和私钥的指针。
- 提供了
rsaEncode
和rsaDecode
方法,用于加密和解密数据。
处理大数运算的辅助类 BigNum
BigNum
类封装了 OpenSSL 的BIGNUM
类型,提供了构造、析构、赋值和转换操作。- 提供了从 Base64、十六进制和二进制字符串构造
BIGNUM
的方法。 - 提供了将
BIGNUM
转换为二进制字符串和 Base64 编码的静态方法。 rsaEncode
和rsaDecode
方法将使用 OpenSSL 的 RSA 功能进行数据加密和解密。rsaEncode
方法将输入的QByteArray
进行 RSA 加密,返回加密后的结果。rsaDecode
方法将输入的QByteArray
进行 RSA 解密,返回解密后的结果。
常量定义
const char* PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n...-----END PRIVATE KEY-----\n";
const char* PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n...-----END PUBLIC KEY-----\n";
const char* SERVER_PRIVATE = "-----BEGIN PRIVATE KEY-----\n...-----END PRIVATE KEY-----\n";
定义了 RSA 的私钥和公钥,通常用于加密和解密操作。
读取配置信息类:RecordFile
类成员变量
QJsonObject m_config; // 存储配置信息的 JSON 对象
QString m_path; // 配置文件的路径
SslTool tool; // 用于加密和解密的工具
构造函数
- 打开文件:
- 使用
QFile
类打开指定路径的文件。如果打开失败,则直接跳出。
- 使用
- 读取文件内容:
- 读取文件的所有内容到
QByteArray
中。如果文件为空,则跳出。
- 读取文件的所有内容到
- 解密数据:
- 使用
tool.rsaDecode(data)
解密读取到的数据。
- 使用
- 处理数据:
- 使用循环去掉不必要的字符,确保数据的有效性。if((int)data[i] >= (int)0x7F || (int)data[i] < (int)0x0A)检查字节的值是否大于等于 127(十六进制的 0x7F),这是 ASCII 字符集中不可打印的字符的起始值。检查字节的值是否小于 10(十六进制的 0x0A),这通常表示换行符(
\n
)之前的字符。这两个条件结合起来,意味着只保留 ASCII 可打印字符(通常是 10 到 126 的范围)如果条件成立,表示找到了一个不可打印的字符,使用data.resize(i)
将data
的大小调整为i
,以保留从开头到当前索引i
的所有字节(有效部分),并丢弃后面的所有字节(无效部分)。
- 使用循环去掉不必要的字符,确保数据的有效性。if((int)data[i] >= (int)0x7F || (int)data[i] < (int)0x0A)检查字节的值是否大于等于 127(十六进制的 0x7F),这是 ASCII 字符集中不可打印的字符的起始值。检查字节的值是否小于 10(十六进制的 0x0A),这通常表示换行符(
- 解析 JSON 数据:
- 使用
QJsonDocument::fromJson
解析 JSON 数据。如果解析成功并且是一个 JSON 对象,则将其存储到m_config
中。
- 使用
- 设置默认值:
- 如果读取或解析失败,则设置默认的配置项,例如用户名、密码、自动登录、记住密码和日期。
save
bool RecordFile::save()
{
QFile file(m_path);
if(file.open(QIODevice::WriteOnly | QIODevice::Truncate) == false)
{
return false; // 打开文件失败
}
QJsonDocument document = QJsonDocument(m_config);
file.write(tool.rsaEncode(document.toJson())); // 加密后写入文件
file.close();
return true; // 保存成功
}
- 打开文件:
- 使用
QFile
打开指定路径的文件,以写入模式和截断模式(即清空文件内容)。
- 使用
- 加密和写入数据:
- 创建一个
QJsonDocument
对象,将m_config
转换为 JSON 格式,并使用tool.rsaEncode
进行加密后写入文件。
- 创建一个
- 关闭文件:
- 关闭文件并返回
true
,表示保存成功。
- 关闭文件并返回
自定义登录窗口类:LoginForm
包含头文件
包含了主要用于创建窗口部件(QWidget),网络请求(QNetworkAccessManager和QNetworkReply),处理JSON数据(QJsonObject,QJsonParseError,QJsonDocument)和桌面服务(QDesktopServices)
功能函数
explicit LoginForm(QWidget* parent = nullptr);
~LoginForm();
virtual bool eventFilter(QObject* watched, QEvent* event);
virtual void timerEvent(QTimerEvent* event);
eventFilter用于处理特定的事件,timerEvent用于处理定时器事件
槽功能函数
void on_logoButton_released();
void on_remberPwd_stateChanged(int state);
void slots_autoLoginCheck_stateChange(int state);
void slots_login_request_finshed(QNetworkReply* reply);
响应按钮释放事件,状态变化事件和网络请求完成事件
信号部分
signals:
void login(const QString& nick, const QByteArray& head);
定义信号 login
,用于在用户登录成功时发射。
成员变量
Ui::LoginForm* ui;
QNetworkAccessManager* net;
InfoForm info;
RecordFile* record;
int auto_login_id;
QPoint position;
MD5加密
const char* MD5_KEY = "*&^%$#@b.v+h-b*g/h@n!h#n$d^ssx,.kl<kl";
const char* HOST = "http://127.0.0.1:19527";
bool LOGIN_STATUS = false;
定义常量 MD5_KEY
用于加密,HOST
是服务器的地址,LOGIN_STATUS
是登录状态的标志。
构造函数
初始化基类QWidget,创建ui和record对象,设置窗口无边框,初始化UI元素的占位符文本,安装事件过滤器以及处理输入框的焦点事件,创建网络访问管理器并连接信号与槽,设置info窗口的属性,并调用load_config加载配置
事件过滤器
事件过滤器是Qt中用于处理事件的一个强大的机制,允许在事件到达之前拦截并处理这些事件
QObject* watched
是被监控的对象,可能是一个文本框(ui->pwdEdit或ui->nameEdit)
通过 if(ui->pwdEdit == watched)
和 else if(ui->nameEdit == watched)
,函数首先检查当前事件是否来自于密码输入框或用户名输入框。
处理 FocusIn
事件:
- 当输入框获得焦点时(即用户点击或选中该输入框),会触发
QEvent::FocusIn
事件。 - 在这种情况下,代码会改变输入框的样式,使文字颜色变为白色,并将背景设置为透明:ui->pwdEdit->setStyleSheet(“color: rgb(251, 251, 251);background-color: transparent;”);
处理 FocusOut
事件:
- 当输入框失去焦点时(即用户点击其他地方或者切换到其他输入框),会触发
QEvent::FocusOut
事件。 - 在这种情况下,代码会检查输入框中是否还有文本。如果没有文本(即用户没有输入任何内容),则将文字颜色恢复为深灰色,并将背景设置为透明:if(ui->pwdEdit->text().size() == 0)
{
ui->pwdEdit->setStyleSheet(“color: rgb(71, 75, 94);background-color: transparent;”);
}
最后,函数调用 QWidget::eventFilter(watched, event)
,将未处理的事件传递给父类的事件过滤器。这是一个好习惯,确保其他事件处理程序也能正常工作。
登录检查
- 创建一个MD5哈希对象QCrytograhicHash md5(QCryptographicHash::Md5);
- 创建网络请求对象QNetworkRequest request;该对象用于发送HTTP请求
- 构造请求URLQString url = QString(HOST) +”/login?”;
- 生成随机的盐值QString salt = QString::number(QRandomeGenerato::global()->bounded(10000,99999));这个盐值是一个随机的四位数
- 获取当前的时间QString time = getTime();
- 生成签名md5.addData((time + MD5_KEY +pwd + salt).toUtf8);QString sign = md5.result().toHex();将时间戳、MD5 密钥、用户输入的密码和盐值拼接在一起,并计算它们的 MD5 哈希值,生成一个签名(
sign
)。这个签名用于验证请求的合法性。 - 设置请求的URLurl += “time=” + time + “&”;
url += “salt=” + salt + “&”;
url += “user=” + user + “&”;
url += “sign=” + sign;将时间、盐值、用户名和签名附加到 URL 中,形成完整的请求 URL。 - 记录用户输入的用户名和密码record->config()[“password”] = ui->pwdEdit->text();
record->config()[“user”] = ui->nameEdit->text();将用户输入的用户名和密码存储到配置中,以便后续使用(例如记住密码功能)。 - 禁用窗口
- 发送Get请求 通过网络访问管理器发送 GET 请求,向服务器请求登录验证
- 返回
加载配置
- 打开配置文件:QFile configFile(“config.json”);
if (!configFile.open(QIODevice::ReadOnly)) {
qWarning(“Could not open config file”);
return;
}- 使用
QFile
类打开名为config.json
的配置文件。如果文件无法打开,则输出警告信息并返回。
- 使用
- 读取文件内容:QByteArray configData = configFile.readAll();
configFile.close();- 读取整个文件的内容到
QByteArray
中,并关闭文件。
- 读取整个文件的内容到
- 解析 JSON 数据:QJsonDocument jsonDoc = QJsonDocument::fromJson(configData);
if (jsonDoc.isNull() || !jsonDoc.isObject()) {
qWarning(“Invalid JSON format”);
return;
}- 使用
QJsonDocument
类从读取的字节数组中解析 JSON 数据。如果解析失败,输出警告信息并返回。
- 使用
- 获取 JSON 对象:QJsonObject jsonObj = jsonDoc.object();
- 获取 JSON 文档中的对象,这样可以方便地访问各个配置项。
- 加载配置项:if (jsonObj.contains(“user”)) {
ui->nameEdit->setText(jsonObj[“user”].toString());
}
if (jsonObj.contains(“password”)) {
ui->pwdEdit->setText(jsonObj[“password”].toString());
}- 检查 JSON 对象中是否包含
user
和password
字段。如果存在,就将其值设置到相应的输入框中。
- 检查 JSON 对象中是否包含
鼠标事件处理
void LoginForm::mouseMoveEvent(QMouseEvent* event)
{
move(event->globalPos() - position);
}
void LoginForm::mousePressEvent(QMouseEvent* event)
{
position = event->globalPos() - this->pos();
}
void LoginForm::mouseReleaseEvent(QMouseEvent* /*event*/)
{
}
这些方法实现了窗口的拖动功能,记录鼠标按下时的位置,并在移动时更新窗口的位置。
class InfoForm : public QWidget
{
Q_OBJECT
public:
explicit InfoForm(QWidget* parent = nullptr);
~InfoForm();
public:
//更新文字 每次弹出信息窗口之前,可以通过此接口来更新窗口信息内容
InfoForm& set_text(const QString& text, const QString& button);
protected:
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
signals:
//按钮按下了(如果是关闭的窗口,自带了close信号)
void button_clicked();
void closed();
protected slots:
void on_connectButton_released();
void on_closeButton_released();
private:
Ui::InfoForm* ui;
QPoint position;
};
