From b17b50b751d3f2355d36aec9c8cf2299eecdcc05 Mon Sep 17 00:00:00 2001 From: amass Date: Thu, 9 Jan 2025 22:23:03 +0800 Subject: [PATCH] fix login rediret retry error. --- Database/Session.cpp | 7 +++- Database/User.h | 4 ++ WebApplication/Application.cpp | 59 +++++++++++++++++++----------- WebApplication/Application.h | 9 +++-- WebApplication/LoginPage.cpp | 24 ++++++------ WebApplication/Restful.cpp | 47 ++++++++++++++---------- WebApplication/model/AuthModel.cpp | 12 +++--- 7 files changed, 99 insertions(+), 63 deletions(-) diff --git a/Database/Session.cpp b/Database/Session.cpp index 88b2669..33aa076 100644 --- a/Database/Session.cpp +++ b/Database/Session.cpp @@ -1,6 +1,7 @@ #include "Session.h" #include "BoostLog.h" #include +#include #include #include #include @@ -11,7 +12,7 @@ std::unique_ptr sqlConnectionPool; bool initialize(const std::string &path) { try { auto connection = std::make_unique(path); - connection->setProperty("show-queries", "true"); + // connection->setProperty("show-queries", "true"); connection->setDateTimeStorage(Wt::Dbo::SqlDateTimeType::DateTime, Wt::Dbo::backend::DateTimeStorage::PseudoISO8601AsText); sqlConnectionPool = std::make_unique(std::move(connection), 10); @@ -73,6 +74,10 @@ void JsonSerializer::act(FieldRef field) } // namespace Dbo } // namespace Wt +User::User(const Wt::Auth::User &user) { + identity = user.identity(Wt::Auth::Identity::LoginName).toUTF8(); +} + DBO_INSTANTIATE_TEMPLATES(User) DBO_INSTANTIATE_TEMPLATES(Task) DBO_INSTANTIATE_TEMPLATES(HomeBox::Item) diff --git a/Database/User.h b/Database/User.h index 9e1ec3f..d60f510 100644 --- a/Database/User.h +++ b/Database/User.h @@ -8,9 +8,13 @@ using AuthInfo = Wt::Auth::Dbo::AuthInfo; class User { public: + User() = default; + User(const Wt::Auth::User &user); template void persist(Action &a) { } + + std::string identity; }; DBO_EXTERN_TEMPLATES(User); diff --git a/WebApplication/Application.cpp b/WebApplication/Application.cpp index ed83aa9..8ee5083 100644 --- a/WebApplication/Application.cpp +++ b/WebApplication/Application.cpp @@ -119,36 +119,36 @@ Application::~Application() { } void Application::authEvent() { + auto app = Amass::Singleton::instance(); + auto &service = app->authService(); + auto token = environment().getCookie(service.authTokenCookieName()); if (m_session->login().loggedIn()) { const Wt::Auth::User &u = m_session->login().user(); LOG(info) << "User " << u.id() << " (" << u.identity(Wt::Auth::Identity::LoginName) << ")" << " logged in."; - auto app = Amass::Singleton::instance(); - auto &service = app->authService(); - auto &env = environment(); - auto token = env.getCookie(service.authTokenCookieName()); if (token == nullptr) { Wt::Http::Cookie cookie(service.authTokenCookieName(), service.createAuthToken(u)); cookie.setDomain(service.authTokenCookieDomain()); cookie.setPath(AuthModel::CookiePath); cookie.setExpires(Wt::WDateTime()); setCookie(cookie); + app->insertCookie(cookie.value(), u); + } + if (m_loginPage) { + if (m_navigationBar != nullptr) { + m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage)); + m_loginPageRef->removeStyleClass("bulma-m-auto"); + m_loginPageRef->removeStyleClass("bulma-container"); + } + } else if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) { + m_loginPage = m_loginPageRef->parent()->removeWidget(m_loginPageRef); + if (m_navigationBar != nullptr) { + m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage)); + m_loginPageRef->removeStyleClass("bulma-m-auto"); + m_loginPageRef->removeStyleClass("bulma-container"); + } } if (m_loginedRedirectUrl.empty()) { - if (m_loginPage) { - if (m_navigationBar != nullptr) { - m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage)); - m_loginPageRef->removeStyleClass("bulma-m-auto"); - m_loginPageRef->removeStyleClass("bulma-container"); - } - } else if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) { - m_loginPage = m_loginPageRef->parent()->removeWidget(m_loginPageRef); - if (m_navigationBar != nullptr) { - m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage)); - m_loginPageRef->removeStyleClass("bulma-m-auto"); - m_loginPageRef->removeStyleClass("bulma-container"); - } - } setInternalPath("/", true); } else { redirect(m_loginedRedirectUrl); @@ -157,6 +157,9 @@ void Application::authEvent() { if (m_navigationBar != nullptr) { m_loginPage = m_navigationBar->removeLoginItem(); } + if (token != nullptr) { + app->removeCookie(*token); + } LOG(info) << "user logged out, internal path: " << internalPath(); if (internalPath() == "/wt/login") { handlePathChange(internalPath()); @@ -258,9 +261,23 @@ const Wt::Auth::PasswordService &Server::passwordService() { return *m_passwordService; } -void Server::insertCookie(const std::string &cookie) { +std::optional Server::user(const std::string &cookie) { + if (m_cookies.contains(cookie)) { + return m_cookies.at(cookie); + } else { + return std::nullopt; + } +} + +void Server::insertCookie(const std::string &cookie, const Wt::Auth::User &user) { if (!m_cookies.contains(cookie)) { - m_cookies.insert(cookie); + m_cookies.insert_or_assign(cookie, user); + } +} + +void Server::removeCookie(const std::string &cookie) { + if (m_cookies.contains(cookie)) { + m_cookies.erase(cookie); } } @@ -281,7 +298,7 @@ Wt::Http::Cookie Server::updateCookie(const std::string &oldCookie, const Wt::Au if (m_cookies.contains(oldCookie)) { // 勾选了记住我 m_cookies.erase(oldCookie); cookie.setMaxAge(std::chrono::seconds(result.newTokenValidity())); - m_cookies.insert(newToken); + m_cookies.insert_or_assign(newToken, result.user()); } else { // 只在会话期间有效 cookie.setExpires(Wt::WDateTime()); } diff --git a/WebApplication/Application.h b/WebApplication/Application.h index 1545094..fc81ca3 100644 --- a/WebApplication/Application.h +++ b/WebApplication/Application.h @@ -4,7 +4,8 @@ #include "Singleton.h" #include #include -#include +#include +#include "Database/User.h" namespace Wt { class WServer; @@ -55,8 +56,10 @@ public: void initializeAuthenticationService(); Wt::Auth::AuthService &authService(); const Wt::Auth::PasswordService &passwordService(); - void insertCookie(const std::string &cookie); + void insertCookie(const std::string &cookie, const Wt::Auth::User &user); Wt::Http::Cookie updateCookie(const std::string &oldCookie, const Wt::Auth::AuthTokenResult &result, bool secure); + std::optional user(const std::string &cookie); + void removeCookie(const std::string &cookie); protected: Server(uint16_t port, const std::string &applicationRoot, const std::string &documentRoot); @@ -68,7 +71,7 @@ private: std::unique_ptr m_authService; std::unique_ptr m_passwordService; - std::unordered_set m_cookies; + std::unordered_map m_cookies; }; } // namespace WebToolkit #endif // __WEBAPPLICATION_H__ \ No newline at end of file diff --git a/WebApplication/LoginPage.cpp b/WebApplication/LoginPage.cpp index 6fdaba6..a4b56ce 100644 --- a/WebApplication/LoginPage.cpp +++ b/WebApplication/LoginPage.cpp @@ -26,7 +26,7 @@ std::unique_ptr LoginPage::createRegistrationView(const Wt::Auth::I } void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt::Auth::AuthService &service) { - using namespace Wt::Auth; + using namespace Wt; std::string emailToken; if (service.emailVerificationEnabled()) { auto redirectPath = service.emailRedirectInternalPath(); @@ -36,24 +36,24 @@ void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt:: } } if (!emailToken.empty()) { - EmailTokenResult result = model()->processEmailToken(emailToken); + Auth::EmailTokenResult result = model()->processEmailToken(emailToken); switch (result.state()) { - case EmailTokenState::Invalid: + case Auth::EmailTokenState::Invalid: displayError(tr("Wt.Auth.error-invalid-token")); break; - case EmailTokenState::Expired: + case Auth::EmailTokenState::Expired: displayError(tr("Wt.Auth.error-token-expired")); break; - case EmailTokenState::UpdatePassword: + case Auth::EmailTokenState::UpdatePassword: letUpdatePassword(result.user(), false); break; - case EmailTokenState::EmailConfirmed: + case Auth::EmailTokenState::EmailConfirmed: displayInfo(tr("Wt.Auth.info-email-confirmed")); - User user = result.user(); + Auth::User user = result.user(); - LoginState state = LoginState::Strong; + Auth::LoginState state = Auth::LoginState::Strong; if (model()->hasMfaStep(user)) { - state = LoginState::RequiresMfa; + state = Auth::LoginState::RequiresMfa; } model()->loginUser(login(), user, state); } @@ -67,10 +67,10 @@ void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt:: return; } - User user = model()->processAuthToken(); - LoginState state = LoginState::Weak; + Auth::User user = model()->processAuthToken(); + Auth::LoginState state = Auth::LoginState::Weak; if (model()->hasMfaStep(user)) { - state = LoginState::RequiresMfa; + state = Auth::LoginState::RequiresMfa; } model()->loginUser(login(), user, state); } \ No newline at end of file diff --git a/WebApplication/Restful.cpp b/WebApplication/Restful.cpp index 531ce00..be58c70 100644 --- a/WebApplication/Restful.cpp +++ b/WebApplication/Restful.cpp @@ -1,6 +1,7 @@ #include "Restful.h" #include "Application.h" #include "Database/Session.h" +#include "Database/User.h" #include "model/AuthModel.h" #include #include @@ -15,6 +16,7 @@ DBO_INSTANTIATE_TEMPLATES(MyMessage) void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) { + using namespace Wt; auto tag = request.urlParam("tag"); // LOG(info) << "path: " << request.path() << ", tag: " << tag << ", server: " << request.hostName(); response.setMimeType("application/json"); @@ -22,30 +24,35 @@ void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt: auto app = Amass::Singleton::instance(); auto &service = app->authService(); if (tag == "verify") { - auto session = Database::session(); - auto enabled = service.authTokenUpdateEnabled(); - boost::scope::scope_exit raii([&enabled, &service] { service.setAuthTokenUpdateEnabled(enabled); }); - service.setAuthTokenUpdateEnabled(false); - Wt::Auth::AuthTokenState state; - Wt::Auth::User user; - if (service.authTokensEnabled()) { - const std::string *token = request.getCookieValue(service.authTokenCookieName()); - if (token != nullptr) { - Wt::Auth::AuthTokenResult result = service.processAuthToken(*token, session->users()); - state = result.state(); - if (state == Wt::Auth::AuthTokenState::Valid) { - user = result.user(); + const std::string *token = request.getCookieValue(service.authTokenCookieName()); + Auth::AuthTokenState state = Auth::AuthTokenState::Invalid; + if (token != nullptr) { + if (auto u = app->user(*token); u) { + state = Auth::AuthTokenState::Valid; + message.user = u->identity; + } else { + Wt::Auth::User user; + auto session = Database::session(); + auto enabled = service.authTokenUpdateEnabled(); + boost::scope::scope_exit raii([&enabled, &service] { service.setAuthTokenUpdateEnabled(enabled); }); + service.setAuthTokenUpdateEnabled(false); + if (service.authTokensEnabled()) { + Auth::AuthTokenResult result = service.processAuthToken(*token, session->users()); + state = result.state(); + if (state == Auth::AuthTokenState::Valid) { + user = result.user(); + message.user = user.identity(Auth::Identity::LoginName).toUTF8(); + } } } + // LOG(info) << "state: " << (int)state << " " << message.user; + } else { + LOG(warning) << "cannot access cookie."; } - if (user.isValid()) { - message.user = user.identity(Wt::Auth::Identity::LoginName).toUTF8(); - } - // LOG(info) << "state: " << (int)state << " " << message.user; message.message = "Hello, World!"; - message.status = state == Wt::Auth::AuthTokenState::Valid ? 0 : 404; + message.status = state == Auth::AuthTokenState::Valid ? 0 : 404; using namespace boost::beast::http; - response.setStatus(static_cast(state == Wt::Auth::AuthTokenState::Valid ? status::ok : status::unauthorized)); + response.setStatus(static_cast(state == Auth::AuthTokenState::Valid ? status::ok : status::unauthorized)); } else { // logout auto domain = request.hostName(); if (domain.find("amass.fun") != std::string::npos) { @@ -54,7 +61,7 @@ void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt: response.addHeader("Set-Cookie", std::format("{}=; path={}; Domain={}; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT", service.authTokenCookieName(), AuthModel::CookiePath, domain)); } - Wt::Dbo::JsonSerializer writer(response.out()); + Dbo::JsonSerializer writer(response.out()); writer.serialize(message); } diff --git a/WebApplication/model/AuthModel.cpp b/WebApplication/model/AuthModel.cpp index aa3a607..66975d0 100644 --- a/WebApplication/model/AuthModel.cpp +++ b/WebApplication/model/AuthModel.cpp @@ -9,22 +9,22 @@ AuthModel::AuthModel(const Wt::Auth::AuthService &baseAuth, Wt::Auth::AbstractUs } Wt::Auth::User AuthModel::processAuthToken() { - using namespace Wt::Auth; + using namespace Wt; if (baseAuth()->authTokensEnabled()) { Wt::WApplication *app = Wt::WApplication::instance(); const Wt::WEnvironment &env = app->environment(); const std::string *token = env.getCookie(baseAuth()->authTokenCookieName()); if (token) { - AuthTokenResult result = baseAuth()->processAuthToken(*token, users()); + Auth::AuthTokenResult result = baseAuth()->processAuthToken(*token, users()); auto server = Amass::Singleton::instance(); auto cookie = server->updateCookie(*token, result, app->environment().urlScheme() == "https"); - if ((result.state() == AuthTokenState::Invalid) || !cookie.value().empty()) { + if ((result.state() == Auth::AuthTokenState::Invalid) || !cookie.value().empty()) { app->setCookie(cookie); } - return result.state() == AuthTokenState::Valid ? result.user() : User(); + return result.state() == Auth::AuthTokenState::Valid ? result.user() : Auth::User(); } } - return User(); + return Auth::User(); } void AuthModel::setRememberMeCookie(const Wt::Auth::User &user) { @@ -39,6 +39,6 @@ void AuthModel::setRememberMeCookie(const Wt::Auth::User &user) { cookie.setSecure(app->environment().urlScheme() == "https"); auto server = Amass::Singleton::instance(); - server->insertCookie(cookie.value()); + server->insertCookie(cookie.value(), user); app->setCookie(cookie); }