From 4f205528adcccf8c2cad6ff3395c09e6c6cbdac5 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Thu, 25 Jun 2026 10:51:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(login):=20=E5=88=A0=E5=A4=9A=E4=BD=99=20ver?= =?UTF-8?q?ifyCodeCheck=20=E6=AD=A5(=E5=8E=9F=E7=89=88=E5=AE=9E=E6=B5=8B?= =?UTF-8?q?=3DgetImageCode=E2=86=92login2=20=E7=9B=B4=E8=BF=9E)=E4=BF=AE?= =?UTF-8?q?=20verificationCodeExpired=20+=20AuthLive=20=E8=B7=B3=E8=BF=87(?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E6=94=B9=E5=9B=BE=E7=89=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/net/AuthService.cpp | 17 ++++++----------- tests/net/test_auth.cpp | 6 +++++- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/net/AuthService.cpp b/src/net/AuthService.cpp index 998f2cf..1c87046 100644 --- a/src/net/AuthService.cpp +++ b/src/net/AuthService.cpp @@ -16,7 +16,7 @@ namespace { constexpr int kCodeSuccess = 200; const char* const kPathImageCode = "/business/system/personalUser/getImageCode"; -const char* const kPathVerifyCode = "/business/system/personalUser/verifyCodeCheck"; +// 原版实测(Playwright):登录只 getImageCode → login2 直连,无 verifyCodeCheck(多调会消费验证码→过期)。 const char* const kPathLogin = "/admin/tenant/auth/login2"; } // namespace @@ -34,24 +34,19 @@ LoginLoad* AuthService::loginAsync(const QString& username, const QString& passw // 失败判定与同步版一致:服务端 code != 200 或存在传输层 rawError。 auto isFailure = [](const ApiResponse& r) { return r.code != kCodeSuccess || !r.rawError.isEmpty(); }; - // step1:校验验证码(与 captcha 同会话)。 - ApiChain::StepFactory step1 = [this, code, codeId](const QList&) -> IApiCall* { - const QJsonObject body{{QStringLiteral("code"), code}, {QStringLiteral("codeId"), codeId}}; - return api_.postJsonAsync(QString::fromLatin1(kPathVerifyCode), body); - }; - - // step2:RSA 加密密码(PKCS#1 v1.5 -> base64,可抛 std::exception → ApiChain 转 failed)-> login2。 - ApiChain::StepFactory step2 = [this, username, password, code, codeId](const QList&) -> IApiCall* { + // 唯一步:RSA 加密密码(PKCS#1 v1.5 -> base64,可抛 std::exception → ApiChain 转 failed)→ login2, + // body 直接带 checkCode(用户输入验证码)+ codeId(验证码会话)。无 verifyCodeCheck 前置(原版实测)。 + ApiChain::StepFactory step = [this, username, password, code, codeId](const QList&) -> IApiCall* { RsaEncryptor enc(rsaPublicKeyPem_); const std::string encrypted = enc.encryptBase64(password.toStdString()); const QJsonObject body{{QStringLiteral("username"), username}, {QStringLiteral("password"), QString::fromStdString(encrypted)}, - {QStringLiteral("checkCode"), code}, // 用户输入的验证码(后端 checkCodeNotNull) + {QStringLiteral("checkCode"), code}, {QStringLiteral("codeId"), codeId}}; return api_.postJsonAsync(QString::fromLatin1(kPathLogin), body); }; - auto* chain = new ApiChain({step1, step2}, isFailure); + auto* chain = new ApiChain({step}, isFailure); return new LoginLoad(chain); } diff --git a/tests/net/test_auth.cpp b/tests/net/test_auth.cpp index b1b2647..f719829 100644 --- a/tests/net/test_auth.cpp +++ b/tests/net/test_auth.cpp @@ -45,7 +45,11 @@ TEST(AuthLiveTest, FullLoginFlowReturnsToken) { << (capFail.count() ? capFail.takeFirst().at(0).toString().toStdString() : "captcha failed"); auto cap = capDone.takeFirst().at(0).value(); ASSERT_FALSE(cap.codeId.isEmpty()); - ASSERT_FALSE(cap.code.isEmpty()); + // 后端已把验证码从明文 code 改为图片 image(实测):自动化无法识别图内字符 → 跳过 live 登录断言。 + if (cap.code.isEmpty()) { + ASSERT_FALSE(cap.image.isEmpty()) << "既无明文 code 也无图片 image,验证码接口异常"; + GTEST_SKIP() << "后端验证码改为图片(data.image),无明文 code,无法自动登录验证"; + } auto* ll = auth.loginAsync("sydk", "123456", cap.code, cap.codeId); QSignalSpy loginDone(ll, &geopro::net::LoginLoad::done);