refactor: 优化 sudo 密码输入体验,增加保存功能

This commit is contained in:
Chenx Dust
2026-03-17 18:49:27 +08:00
parent 103fe3e17b
commit d5aaab5abc
10 changed files with 229 additions and 43 deletions

View File

@@ -39,6 +39,7 @@ set(SOURCE_FILES
extrasettingwindow/extrasettingwindow.cpp
settingwindow/settingwindow.cpp
loginwindow/loginwindow.cpp
sudowindow/sudowindow.cpp
ssologinwebview/ssologinwebview.cpp
authinfowindow/authinfowindow.cpp
graphcaptchawindow/graphcaptchawindow.cpp

View File

@@ -10,6 +10,7 @@
#include <QPointer>
#include "loginwindow/loginwindow.h"
#include "sudowindow/sudowindow.h"
#include "ssologinwebview/ssologinwebview.h"
#include "zjuconnectcontroller/zjuconnectcontroller.h"
#include "settingwindow/settingwindow.h"
@@ -98,6 +99,7 @@ private:
QPointer<SettingWindow> settingWindow;
QPointer<LoginWindow> loginWindow;
QPointer<SudoWindow> sudoWindow;
QPointer<SsoLoginWebView> ssoLoginWebView;
QPointer<GraphCaptchaWindow> graphCaptchaWindow;

44
sudowindow/sudowindow.cpp Normal file
View File

@@ -0,0 +1,44 @@
#include "sudowindow.h"
#include <QPushButton>
#include <QKeyEvent>
SudoWindow::SudoWindow(QWidget *parent)
: QDialog(parent),
ui(new Ui::SudoWindow)
{
ui->setupUi(this);
setWindowModality(Qt::WindowModal);
setAttribute(Qt::WA_DeleteOnClose);
connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
[&]()
{
emit sudo(ui->passwordLineEdit->text(), ui->savePasswordCheckBox->isChecked());
emit accept();
}
);
connect(ui->passwordVisibleCheckBox, &QCheckBox::checkStateChanged,
[&](Qt::CheckState state)
{
ui->passwordLineEdit->setEchoMode(state == Qt::Checked ? QLineEdit::Normal : QLineEdit::Password);
}
);
}
SudoWindow::~SudoWindow()
{
delete ui;
}
void SudoWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
ui->buttonBox->button(QDialogButtonBox::Ok)->click();
else if (event->key() == Qt::Key_Escape)
reject();
else
QDialog::keyPressEvent(event);
}

22
sudowindow/sudowindow.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <QDialog>
#include "ui_sudowindow.h"
class SudoWindow : public QDialog
{
Q_OBJECT
public:
SudoWindow(QWidget *parent = nullptr);
~SudoWindow() override;
void keyPressEvent(QKeyEvent *event) override;
signals:
void sudo(const QString& password, bool save);
private:
Ui::SudoWindow *ui;
};

101
sudowindow/sudowindow.ui Normal file
View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SudoWindow</class>
<widget class="QDialog" name="SudoWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>385</width>
<height>164</height>
</rect>
</property>
<property name="windowTitle">
<string>Sudo</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>需要 root 权限</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="2">
<widget class="QCheckBox" name="passwordVisibleCheckBox">
<property name="text">
<string>显示密码</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="savePasswordCheckBox">
<property name="text">
<string>本次会话内保存</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="3">
<widget class="QLineEdit" name="passwordLineEdit">
<property name="echoMode">
<enum>QLineEdit::EchoMode::Password</enum>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label2">
<property name="text">
<string>请输入 sudo 密码:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>passwordLineEdit</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SudoWindow</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -229,23 +229,6 @@ bool Utils::isRunningAsAdmin()
#endif
}
bool Utils::promptForSudoPassword(QString &password, QWidget *parent)
{
bool ok = false;
QString text = QInputDialog::getText(parent,
"需要 Root 权限",
"请输入 sudo 密码:",
QLineEdit::Password,
"",
&ok);
if (!ok)
{
return false;
}
password = text;
return true;
}
bool Utils::relaunchAsAdmin()
{
QString program = QCoreApplication::applicationFilePath();

View File

@@ -44,8 +44,6 @@ namespace Utils
bool relaunchAsAdmin();
bool promptForSudoPassword(QString &password, QWidget *parent = nullptr);
void clearClientData(const QString &profileId);
QString getClientDataPath(const QString &profileId);

View File

@@ -35,7 +35,11 @@ ZjuConnectController::ZjuConnectController(QWidget* parent) : QObject(parent)
logStream->flush();
}
if (output.contains("Graph check code saved to "))
if (output.contains("SUDO_ASK_PASS"))
{
emit askSudoPass();
}
else if (output.contains("Graph check code saved to "))
{
emit graphCaptcha(graphFile);
}
@@ -432,34 +436,18 @@ void ZjuConnectController::start(
#if defined(Q_OS_UNIX)
QString sudoPassword;
bool useSudo = false;
if (tunMode && !Utils::isRunningAsAdmin())
{
if (!Utils::promptForSudoPassword(sudoPassword, qobject_cast<QWidget *>(parent())))
{
emit outputRead(timeString + " 已取消 sudo 密码输入");
}
else
{
QStringList sudoArgs;
sudoArgs << "-p" << "SUDO_ASK_PASS";
sudoArgs << "-S";
sudoArgs << programToStart;
sudoArgs << finalArgs;
sudoArgs << programToStart << finalArgs;
programToStart = "sudo";
finalArgs = sudoArgs;
useSudo = true;
emit outputRead(timeString + " 使用 sudo 提升权限启动核心");
}
enteredSudoPassword = false;
}
#endif
zjuConnectProcess->start(programToStart, finalArgs);
#if defined(Q_OS_UNIX)
if (useSudo)
{
zjuConnectProcess->write((sudoPassword + "\n").toUtf8());
}
#endif
zjuConnectProcess->waitForStarted();
if (zjuConnectProcess->state() == QProcess::NotRunning)
{

View File

@@ -88,6 +88,8 @@ signals:
void casAuth();
void askSudoPass();
void finished();
void write(const QByteArray &data);
@@ -102,6 +104,11 @@ private:
QFile *logFile = nullptr;
QTextStream *logStream = nullptr;
bool stopRequested = false;
public:
bool savedSudoPassword;
bool enteredSudoPassword;
QString sudoPassword;
};
#endif //ZJUCONNECTCONTROLLER_H

View File

@@ -4,6 +4,7 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "sudowindow/sudowindow.h"
#include "loginwindow/loginwindow.h"
#include "utils/utils.h"
#include "zjuconnectcontroller/zjuconnectcontroller.h"
@@ -36,6 +37,45 @@ void MainWindow::initZjuConnect()
}
});
connect(zjuConnectController, &ZjuConnectController::askSudoPass, this,
[&]()
{
if (zjuConnectController->savedSudoPassword)
{
if (zjuConnectController->enteredSudoPassword)
{
addLog("sudo 密码可能有误,不使用记住的密码");
zjuConnectController->savedSudoPassword = false;
zjuConnectController->sudoPassword.clear();
}
else
{
zjuConnectController->enteredSudoPassword = true;
emit WriteToProcess(zjuConnectController->sudoPassword.toUtf8() + "\n");
return;
}
}
sudoWindow = new SudoWindow(this);
connect(sudoWindow, &SudoWindow::sudo, this, [&](const QString &password, bool save)
{
if (password.isEmpty())
{
zjuConnectController->stop();
}
else
{
if (save)
{
zjuConnectController->savedSudoPassword = true;
zjuConnectController->sudoPassword = password;
}
zjuConnectController->enteredSudoPassword = true;
emit WriteToProcess(password.toUtf8() + "\n");
}
});
sudoWindow->show();
});
connect(zjuConnectController, &ZjuConnectController::graphCaptcha, this,
[&](const QString &graphFile) {
addLog("需要图形验证码");