diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3e5f49c --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: Microsoft \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ae166c0..816a0d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ find_package(Qt6 COMPONENTS Network Svg Core5Compat + WebEngineWidgets REQUIRED ) @@ -37,6 +38,7 @@ set(SOURCE_FILES extrasettingwindow/extrasettingwindow.cpp settingwindow/settingwindow.cpp loginwindow/loginwindow.cpp + ssologinwebview/ssologinwebview.cpp utils/utils.cpp utils/proxysettings.cpp utils/coreversion.cpp @@ -88,6 +90,7 @@ target_link_libraries(${PROJECT_NAME} Qt::Network Qt::Svg Qt::Core5Compat + Qt::WebEngineWidgets SingleApplication::SingleApplication ${PLATFORM_LIBS} ) diff --git a/mainwindow.cpp b/mainwindow.cpp index 16cb7d1..342fd1f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -9,12 +9,9 @@ #include "mainwindow.h" -#include - #include "ui_mainwindow.h" -#include "zjuconnectcontroller/zjuconnectcontroller.h" #include "utils/utils.h" - +#include "zjuconnectcontroller/zjuconnectcontroller.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), @@ -22,15 +19,10 @@ MainWindow::MainWindow(QWidget *parent) : { zjuConnectController = nullptr; - process = new QProcess(this); - processForL2tp = new QProcess(this); - processForL2tpCheck = new QProcess(this); - processForWebLogin = new QProcess(this); settings = new QSettings( QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/config.ini", QSettings::IniFormat ); - l2tpCheckTimer = nullptr; diagnosisContext = nullptr; upgradeSettings(); @@ -325,6 +317,10 @@ void MainWindow::upgradeSettings() { Utils::resetDefaultSettings(*settings); } + else if (configVersion == 4) + { + settings->setValue("ZJUConnect/Protocol", "easyconnect"); + } else if (configVersion < Utils::CONFIG_VERSION) { QMessageBox msgBox; diff --git a/mainwindow.h b/mainwindow.h index 21192c6..ee27e94 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -9,7 +9,7 @@ #include #include "loginwindow/loginwindow.h" -#include "extrasettingwindow/extrasettingwindow.h" +#include "ssologinwebview/ssologinwebview.h" #include "zjuconnectcontroller/zjuconnectcontroller.h" #include "settingwindow/settingwindow.h" @@ -71,16 +71,12 @@ private: QNetworkAccessManager *checkUpdateNAM; QNetworkAccessManager *checkCoreUpdateNAM; QSettings *settings; - QProcess *process; - QProcess *processForL2tp; - QProcess *processForL2tpCheck; - QProcess *processForWebLogin; - QTimer *l2tpCheckTimer; QObject *diagnosisContext; SettingWindow *settingWindow; - LoginWindow *login_window; + LoginWindow *loginWindow; + SsoLoginWebView *ssoLoginWebView; bool isFirstTimeSetMode; diff --git a/mainwindow.ui b/mainwindow.ui index a59e652..1e91ba7 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -78,6 +78,12 @@ Line 2 QSizePolicy::Policy::Minimum + + + 0 + 0 + + @@ -140,7 +146,7 @@ Line 2 0 0 600 - 33 + 30 @@ -189,6 +195,14 @@ Line 2 设置 + + + SSO 登录 + + + 打开校园网 SSO 登录窗口 + + 清除系统代理 diff --git a/settingwindow/settingwindow.cpp b/settingwindow/settingwindow.cpp index 7b49189..5948d03 100644 --- a/settingwindow/settingwindow.cpp +++ b/settingwindow/settingwindow.cpp @@ -41,15 +41,17 @@ SettingWindow::SettingWindow(QWidget *parent, QSettings *inputSettings) : extraSettingWindow->show(); }); - connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, [&](){ - if (!Utils::credentialCheck(ui->usernameLineEdit->text(), ui->passwordLineEdit->text())) + connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, [&]() { + if (!(ui->atrustRadioButton->isChecked() && ui->casRadioButton->isChecked()) && + !Utils::credentialCheck(ui->usernameLineEdit->text(), ui->passwordLineEdit->text())) return; applySettings(); accept(); }); - connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, [&](){ - if (!Utils::credentialCheck(ui->usernameLineEdit->text(), ui->passwordLineEdit->text())) + connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, [&]() { + if (!(ui->atrustRadioButton->isChecked() && ui->casRadioButton->isChecked()) && + !Utils::credentialCheck(ui->usernameLineEdit->text(), ui->passwordLineEdit->text())) return; applySettings(); loadSettings(); @@ -103,19 +105,6 @@ SettingWindow::SettingWindow(QWidget *parent, QSettings *inputSettings) : QFile::copy(settings->fileName(), filename); }); - connect(ui->tunCheckBox, &QCheckBox::toggled, - [&](bool checked) - { - ui->routeCheckBox->setEnabled(checked); - ui->dnsHijackCheckBox->setEnabled(checked); - }); - - connect(ui->dnsAutoCheckBox, &QCheckBox::toggled, - [&](bool checked) - { - ui->dnsLineEdit->setEnabled(!checked); - }); - connect(ui->passwordVisibleCheckBox, &QCheckBox::checkStateChanged, [&](Qt::CheckState state) { @@ -166,6 +155,16 @@ void SettingWindow::loadSettings() ui->shadowsocksUrlLineEdit->setText(settings->value("ZJUConnect/ShadowsocksURL").toString()); ui->dialDirectProxyLineEdit->setText(settings->value("ZJUConnect/DialDirectProxy").toString()); + if (settings->value("ZJUConnect/Protocol").toString() == "atrust") + ui->atrustRadioButton->setChecked(true); + else + ui->easyconnectRadioButton->setChecked(true); + ui->loginDomainLineEdit->setText(settings->value("ZJUConnect/LoginDomain").toString()); + if (settings->value("ZJUConnect/AuthType").toString() == "cas") + ui->casRadioButton->setChecked(true); + else + ui->pswRadioButton->setChecked(true); + ui->casLoginUrlLineEdit->setText(settings->value("ZJUConnect/CasLoginURL").toString()); ui->multiLineCheckBox->setChecked(settings->value("ZJUConnect/MultiLine").toBool()); ui->keepAliveCheckBox->setChecked(settings->value("ZJUConnect/KeepAlive").toBool()); @@ -224,6 +223,10 @@ void SettingWindow::applySettings() settings->setValue("ZJUConnect/ShadowsocksURL", ui->shadowsocksUrlLineEdit->text()); settings->setValue("ZJUConnect/DialDirectProxy", ui->dialDirectProxyLineEdit->text()); + settings->setValue("ZJUConnect/Protocol", ui->atrustRadioButton->isChecked() ? "atrust" : "easyconnect"); + settings->setValue("ZJUConnect/LoginDomain", ui->loginDomainLineEdit->text()); + settings->setValue("ZJUConnect/AuthType", ui->casRadioButton->isChecked() ? "cas" : "psw"); + settings->setValue("ZJUConnect/CasLoginURL", ui->casLoginUrlLineEdit->text()); settings->setValue("ZJUConnect/MultiLine", ui->multiLineCheckBox->isChecked()); settings->setValue("ZJUConnect/KeepAlive", ui->keepAliveCheckBox->isChecked()); diff --git a/settingwindow/settingwindow.ui b/settingwindow/settingwindow.ui index e73c180..3c7200a 100644 --- a/settingwindow/settingwindow.ui +++ b/settingwindow/settingwindow.ui @@ -6,8 +6,8 @@ 0 0 - 486 - 500 + 514 + 519 @@ -29,94 +29,7 @@ 通用 - - - - - 保存的信息 - - - - - - Qt::Orientation::Horizontal - - - QSizePolicy::Policy::Preferred - - - - 120 - 0 - - - - - - - - QLineEdit::EchoMode::Password - - - 可选,以通过 TOTP 验证器 - - - - - - - 密码 - - - - - - - 账号 - - - - - - - TOTP 密钥 - - - - - - - QLineEdit::EchoMode::Password - - - VPN 密码 - - - - - - - - - - - - - - VPN 账号 - - - - - - - - - - - - - + @@ -217,6 +130,12 @@ Qt::Orientation::Horizontal + + + 0 + 0 + + @@ -270,6 +189,9 @@ + + false + 86400 @@ -299,6 +221,232 @@ + + + 登录 + + + + + + 认证信息(aTrust) + + + + + + 认证方式 + + + + + + + 单点认证 + + + + + + + 密码 + + + true + + + + + + + + + + 可自动获取,默认 local + + + + + + + 获取认证方式 + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Preferred + + + + 120 + 0 + + + + + + + + 登录域 + + + + + + + 单点认证地址 + + + + + + + false + + + + + + 可自动获取,仅用于单点登录 + + + + + + + + + + 协议 + + + + + + aTrust + + + true + + + + + + + EasyConnect + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + 保存的信息 + + + + + + QLineEdit::EchoMode::Password + + + 可选,以通过 TOTP 验证器 + + + + + + + 账号 + + + + + + + QLineEdit::EchoMode::Password + + + VPN 密码 + + + + + + + + + + + + + + VPN 账号 + + + + + + + TOTP 密钥 + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Preferred + + + + 120 + 0 + + + + + + + + + + + + + + + 密码 + + + + + + + + 核心 @@ -407,7 +555,7 @@ - vpn.hitsz.edu.cn + trust.hitsz.edu.cn @@ -622,5 +770,101 @@ + + atrustRadioButton + toggled(bool) + authInfoGroupBox + setEnabled(bool) + + + 255 + 72 + + + 256 + 186 + + + + + casRadioButton + toggled(bool) + casLoginUrlLineEdit + setEnabled(bool) + + + 397 + 221 + + + 327 + 247 + + + + + dnsAutoCheckBox + toggled(bool) + dnsLineEdit + setEnabled(bool) + + + 445 + 85 + + + 282 + 85 + + + + + tunCheckBox + toggled(bool) + routeCheckBox + setEnabled(bool) + + + 108 + 379 + + + 256 + 379 + + + + + tunCheckBox + toggled(bool) + dnsHijackCheckBox + setEnabled(bool) + + + 108 + 379 + + + 405 + 379 + + + + + autoReconnectCheckBox + toggled(bool) + reconnectTimeSpinBox + setEnabled(bool) + + + 463 + 173 + + + 311 + 173 + + + diff --git a/ssologinwebview/ssologinwebview.cpp b/ssologinwebview/ssologinwebview.cpp new file mode 100644 index 0000000..3dad278 --- /dev/null +++ b/ssologinwebview/ssologinwebview.cpp @@ -0,0 +1,82 @@ +#include "ssologinwebview.h" +#include "ui_ssologinwebview.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +SsoLoginWebView::SsoLoginWebView(QWidget *parent) + : QDialog(parent) + , ui(new Ui::SsoLoginWebView) +{ + ui->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose); + setModal(true); + + setupConnections(); +} + +SsoLoginWebView::~SsoLoginWebView() +{ + delete ui; +} + +void SsoLoginWebView::setupConnections() +{ + connect(ui->backButton, &QToolButton::clicked, ui->webEngineView, &QWebEngineView::back); + connect(ui->forwardButton, &QToolButton::clicked, ui->webEngineView, &QWebEngineView::forward); + connect(ui->reloadButton, &QToolButton::clicked, ui->webEngineView, &QWebEngineView::reload); + + connect(ui->addressLineEdit, &QLineEdit::returnPressed, this, [&]() + { + const QUrl target = QUrl::fromUserInput(ui->addressLineEdit->text()); + ui->webEngineView->load(target); + }); + + connect(ui->webEngineView, &QWebEngineView::urlChanged, this, [&](const QUrl &url) + { + ui->addressLineEdit->setText(url.toString()); + }); + + connect(ui->webEngineView->page(), &QWebEnginePage::navigationRequested, this, + [&](QWebEngineNavigationRequest &request) { + if (request.navigationType() == QWebEngineNavigationRequest::NavigationType::RedirectNavigation && + request.url().host() == callbackServerHost) + { + QUrlQuery queries(request.url()); + loginCompleted(queries.queryItemValue("ticket")); + request.reject(); + this->close(); + } + else + request.accept(); + }); + + + connect(ui->dialogButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +void SsoLoginWebView::setInitialUrl(const QUrl &url) +{ + if (!url.isEmpty()) + { + ui->addressLineEdit->setText(url.toString()); + ui->webEngineView->load(url); + } +} + +void SsoLoginWebView::setCallbackServerHost(const QString &host) +{ + callbackServerHost = host; +} + +QUrl SsoLoginWebView::currentUrl() const +{ + return ui->webEngineView->url(); +} diff --git a/ssologinwebview/ssologinwebview.h b/ssologinwebview/ssologinwebview.h new file mode 100644 index 0000000..46f5a0e --- /dev/null +++ b/ssologinwebview/ssologinwebview.h @@ -0,0 +1,35 @@ +#ifndef SSOLOGINWEBVIEW_H +#define SSOLOGINWEBVIEW_H + +#include +#include +#include + +namespace Ui +{ +class SsoLoginWebView; +} + +class SsoLoginWebView : public QDialog +{ + Q_OBJECT + +public: + explicit SsoLoginWebView(QWidget *parent = nullptr); + ~SsoLoginWebView() override; + + void setInitialUrl(const QUrl &url); + void setCallbackServerHost(const QString &host); + QUrl currentUrl() const; + +signals: + void loginCompleted(const QString &ticket); + +private: + void setupConnections(); + + Ui::SsoLoginWebView *ui; + QString callbackServerHost; +}; + +#endif // SSOLOGINWEBVIEW_H diff --git a/ssologinwebview/ssologinwebview.ui b/ssologinwebview/ssologinwebview.ui new file mode 100644 index 0000000..3d97c88 --- /dev/null +++ b/ssologinwebview/ssologinwebview.ui @@ -0,0 +1,99 @@ + + + SsoLoginWebView + + + + 0 + 0 + 960 + 640 + + + + SSO 登录 + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 后退 + + + ⬅️ + + + + + + + 前进 + + + ➡️ + + + + + + + 重新载入 + + + 🔄 + + + + + + + + + + + + + + 0 + 0 + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Close + + + + + + + + QWebEngineView + QWidget +
QtWebEngineWidgets/QWebEngineView
+
+
+ + +
diff --git a/utils/utils.cpp b/utils/utils.cpp index cb8f0cc..96bcfa3 100644 --- a/utils/utils.cpp +++ b/utils/utils.cpp @@ -306,7 +306,7 @@ void Utils::resetDefaultSettings(QSettings& settings) settings.setValue("Common/SystemProxyBypass", ""); - settings.setValue("ZJUConnect/ServerAddress", "vpn.hitsz.edu.cn"); + settings.setValue("ZJUConnect/ServerAddress", "trust.hitsz.edu.cn"); settings.setValue("ZJUConnect/ServerPort", 443); settings.setValue("ZJUConnect/DNS", ""); settings.setValue("ZJUConnect/DNSAuto", true); @@ -317,6 +317,9 @@ void Utils::resetDefaultSettings(QSettings& settings) settings.setValue("ZJUConnect/ShadowsocksURL", ""); settings.setValue("ZJUConnect/DialDirectProxy", ""); + settings.setValue("ZJUConnect/Protocol", "atrust"); + settings.setValue("ZJUConnect/LoginDomain", "hitcas"); + settings.setValue("ZJUConnect/AuthType", "cas"); settings.setValue("ZJUConnect/MultiLine", false); settings.setValue("ZJUConnect/KeepAlive", false); diff --git a/utils/utils.h b/utils/utils.h index 78bafdc..8455174 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -14,7 +14,7 @@ namespace Utils const inline QString APP_NAME("EZ4Connect"); - constexpr inline int CONFIG_VERSION = 4; + constexpr inline int CONFIG_VERSION = 5; QString ConsoleOutputToQString(const QByteArray &byteArray); diff --git a/zjuconnectcontroller/zjuconnectcontroller.cpp b/zjuconnectcontroller/zjuconnectcontroller.cpp index aa3975f..4ea2c6d 100644 --- a/zjuconnectcontroller/zjuconnectcontroller.cpp +++ b/zjuconnectcontroller/zjuconnectcontroller.cpp @@ -74,6 +74,10 @@ ZjuConnectController::ZjuConnectController() void ZjuConnectController::start( const QString& program, + const QString& protocol, + const QString& authType, + const QString& loginDomain, + const QString& casTicket, const QString& username, const QString& password, const QString& totpSecret, @@ -107,6 +111,30 @@ void ZjuConnectController::start( { QStringList args; + if (!protocol.isEmpty()) + { + args.append("-protocol"); + args.append(protocol); + } + + if (!authType.isEmpty()) + { + args.append("-auth-type"); + args.append("auth/" + authType); + } + + if (!loginDomain.isEmpty()) + { + args.append("-login-domain"); + args.append(loginDomain); + } + + if (!casTicket.isEmpty()) + { + args.append("-cas-ticket"); + args.append(casTicket); + } + if (!server.isEmpty()) { args.append("-server"); @@ -255,10 +283,19 @@ void ZjuConnectController::start( QString timeString = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); emit outputRead(timeString + " VPN 启动!参数:" + args.join(' ')); - QStringList credentialList({ - "-username", username, - "-password", password, - }); + QStringList credentialList; + + if (!username.isEmpty()) + { + credentialList.append("-username"); + credentialList.append(username); + } + + if (!password.isEmpty()) + { + credentialList.append("-password"); + credentialList.append(password); + } if (!totpSecret.isEmpty()) { diff --git a/zjuconnectcontroller/zjuconnectcontroller.h b/zjuconnectcontroller/zjuconnectcontroller.h index e77dcc1..3df9aa4 100644 --- a/zjuconnectcontroller/zjuconnectcontroller.h +++ b/zjuconnectcontroller/zjuconnectcontroller.h @@ -27,6 +27,10 @@ public: void start( const QString& program, + const QString& protocol, + const QString& authType, + const QString& loginDomain, + const QString& casTicket, const QString& username, const QString& password, const QString& totpSecret, diff --git a/zjuconnectmode.cpp b/zjuconnectmode.cpp index 6730d9e..b2a2afe 100644 --- a/zjuconnectmode.cpp +++ b/zjuconnectmode.cpp @@ -113,8 +113,10 @@ void MainWindow::initZjuConnect() QString username_ = settings->value("Credential/Username", "").toString(); QString password_ = QByteArray::fromBase64(settings->value("Credential/Password", "").toString().toUtf8()); + QString protocol = settings->value("ZJUConnect/Protocol", "easyconnect").toString(); + QString authtype = settings->value("ZJUConnect/AuthType", "psw").toString(); - auto startZjuConnect = [this](const QString &username, const QString &password) { + auto startZjuConnect = [this](const QString &username, const QString &password, const QString &casTicket) { QString program_path = Utils::getCorePath(); QString bind_prefix = settings->value("ZJUConnect/OutsideAccess", false).toBool() ? "[::]:" : "127.0.0.1:"; @@ -128,14 +130,14 @@ void MainWindow::initZjuConnect() } zjuConnectController->start( - program_path, - username, - password, + program_path, settings->value("ZJUConnect/Protocol").toString(), + settings->value("ZJUConnect/AuthType").toString(), + settings->value("ZJUConnect/LoginDomain").toString(), casTicket, username, password, settings->value("Credential/TOTPSecret").toString(), settings->value("ZJUConnect/ServerAddress").toString(), settings->value("ZJUConnect/ServerPort").toInt(), settings->value("ZJUConnect/DNS").toString(), - settings->value("ZJUConnect/DNSAuto").toBool(), + settings->value("ZJUConnect/DNSAuto").toBool(), settings->value("ZJUConnect/SecondaryDNS").toString(), settings->value("ZJUConnect/DNSTTL").toInt(), bind_prefix + QString::number(settings->value("ZJUConnect/Socks5Port").toInt()), @@ -156,17 +158,32 @@ void MainWindow::initZjuConnect() settings->value("ZJUConnect/TcpPortForwarding").toString(), settings->value("ZJUConnect/UdpPortForwarding").toString(), settings->value("ZJUConnect/CustomDNS", "").toString(), - settings->value("ZJUConnect/CustomProxyDomain", "").toString(), - settings->value("ZJUConnect/ExtraArguments", "").toString() - ); + settings->value("ZJUConnect/CustomProxyDomain", "").toString(), + settings->value("ZJUConnect/ExtraArguments", "").toString()); }; - if (username_.isEmpty() || password_.isEmpty()) + if (protocol == "atrust" && authtype == "cas") { - login_window = new LoginWindow(this); - login_window->setDetail(username_, password_); + ssoLoginWebView = new SsoLoginWebView(this); + connect(ssoLoginWebView, &SsoLoginWebView::loginCompleted, [=](const QString &ticket) { + startZjuConnect(username_, password_, ticket); + }); - connect(login_window, &LoginWindow::login, this, + const QString ssoUrl = settings->value("ZJUConnect/CasLoginURL", "about:blank").toString(); + + addLog(QStringLiteral("单点登录:") + ssoUrl); + ssoLoginWebView->setInitialUrl(QUrl::fromUserInput(ssoUrl)); + ssoLoginWebView->setCallbackServerHost(settings->value("ZJUConnect/ServerAddress", "trust.hitsz.edu.cn").toString()); + ssoLoginWebView->show(); + ssoLoginWebView->raise(); + ssoLoginWebView->activateWindow(); + } + else if (username_.isEmpty() || password_.isEmpty()) + { + loginWindow = new LoginWindow(this); + loginWindow->setDetail(username_, password_); + + connect(loginWindow, &LoginWindow::login, this, [&, startZjuConnect](const QString& username, const QString& password, bool saveDetail) { if (saveDetail) @@ -175,14 +192,14 @@ void MainWindow::initZjuConnect() settings->setValue("Credential/Password", QString(password.toUtf8().toBase64())); settings->sync(); } - startZjuConnect(username, password); + startZjuConnect(username, password, ""); } ); - login_window->show(); + loginWindow->show(); } else { - startZjuConnect(username_, password_); + startZjuConnect(username_, password_, ""); } } else