You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt 5.12.4桌面应用Windows环境下SSO登录无法稳定获取SAMLResponse问题

QtWebEngine实现Microsoft SSO登录的问题排查与解决

问题描述

在Qt 5.12.4桌面应用中,基于QtWebEngine加载Microsoft身份认证页面实现SSO登录时,遇到以下问题:

  • 发起指定SAML请求后,未获取到预期的SAMLResponse令牌,反而返回一段包含自动重定向表单的HTML响应
  • 功能间歇性正常,偶尔能成功获取SAMLResponse

相关信息

请求链接

https://login.microsoftonline.com/54b90cf0-8817-42de-893f-9d32076b4a9b/saml2?SAMLRequest=jZFPa8MwDMW%2FSvB9cf64xDNJwG1aKHQw2nWH3Uym0UBip5ZS9vGXpORUGAWdhJ70fk85mq7tlR7oYo9wHQAp%2BO1aiwUbvFXOYIPKmg5QUa1O%2Bu2gkjBSHZD5NmRYsK8KtpOiklJqna6F2MRrKUSarbTYyiSOqtctCz7BY%2BNswUbxqEEcYG%2BRjKWxFSXpSzSW%2BIhjFaUqlmG2yr6muXeD2NygYD%2BmRWCBRgRP46aNszh04E%2Fgb00N5%2BOhYBeiHhXnvfNk2qsJR%2FMEYe06juj4RMongocl7I6s5iz%2BB%2B%2B9I1e7lgU752uYc1vclfkM5p8J0CweWPmM7SXvnN9PlDl%2FfFz5Bw%3D%3D

异常响应HTML

<!-- This jsp is designed due to caching issue in Chrome. because after login when call was redirected to home servlet at that time 
    it was making call of cached data images/js/css to revalidate it (304 status code) -->
<html>
<head>
    <title>CompnayName</title>
    <script language="javascript" type="text/javascript">
        localStorage.setItem("isFromLogin", "true");
        function redirectToAdoddleHome(){
            document.frmHome.action_id.value=1;
            document.frmHome.submit();
        }
    </script>
    </head>
    <body onload="redirectToAdoddleHome();">
    
    <form name="frmHome" id="frmHome" action="/adoddle/home" method="get">
        <input type="hidden" id="action_id" name="action_id" value="1">
    </form> 
    
    </body>
</html>

核心Qt代码

QString AsiteSSOManager::CallLoginToRP(const QString& strSSOADFSURL, const QString& strSSOTargetURL)
{
    QString retValHTML = "";
    m_view = new QWebEngineView();
    m_view->showFullScreen();
    m_view->page()->profile()->cookieStore()->deleteAllCookies();
    m_view->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
    m_view->page()->profile()->setHttpCacheType(QWebEngineProfile::NoCache);
    QString strURL = "";
    if (strSSOADFSURL.contains("?"))
    {
        strURL = QString("%1%2%3").arg(strSSOADFSURL).arg("&loginToRp=").arg(strSSOTargetURL);
    }
    else
    {
        strURL = QString("%1%2%3").arg(strSSOADFSURL).arg(LOGIN_TO_RP).arg(strSSOTargetURL);
    }
    
    connect(m_view, &QWebEngineView::loadFinished, this, &AsiteSSOManager::OnLoginToRPLoadFinished);
    connect(m_view->page(), &QWebEnginePage::authenticationRequired, this, &AsiteSSOManager::HandleAuthenticationRequired);
    m_view->load(strURL);

    if (strSSOADFSURL.contains("?"))
    {
        m_view->show();
    }

    m_timer.start();
    QEventLoop loop;
    connect(this, &AsiteSSOManager::RequiredHTMLReceived, &loop, &QEventLoop::quit);
    loop.exec();

    retValHTML = m_loginToRPResponse;
    m_view->stop();
    m_view->page()->profile()->cookieStore()->deleteAllCookies();
    m_timer.stop();
    if (m_view)
    {
        m_view->deleteLater();
    }
    return retValHTML;
}

void AsiteSSOManager::OnTimeOut()
{
    //emit RequiredHTMLReceived();
}

void AsiteSSOManager::OnLoginToRPLoadFinished(bool ok)
{
    if (ok)
    {
        m_view->page()->toHtml(
            [&](const QString &html)
        {
            if (html.contains("SAMLResponse") || html.contains("aSessionId"))
            {
                m_loginToRPResponse = html; 
                emit RequiredHTMLReceived();
                //qDebug() << m_loginToRPResponse;
            }
        });
    }
}

原因分析

  1. 页面加载时机判断偏差:loadFinished信号触发时,页面的JavaScript可能尚未完成自动重定向逻辑,此时获取HTML只能拿到中间的重定向表单页面,而非最终包含SAMLResponse的页面。
  2. 缓存清理不彻底:虽然代码中设置了禁用持久化Cookie和HTTP缓存,但未主动清理内存缓存,导致身份认证状态存在残留,引发间歇性异常。
  3. 事件循环退出逻辑不完善:仅当HTML中包含SAMLResponse或aSessionId时才退出事件循环,若遇到重定向页面则无后续处理,导致事件循环可能一直阻塞或返回错误结果。
  4. QtWebEngine版本兼容性:Qt 5.12.4的WebEngine基于较旧的Chromium版本(约Chromium 69),对现代SAML认证流程中的JavaScript特性支持不足,存在兼容性问题。

解决方案

1. 调整页面内容检查时机

延迟获取HTML给JavaScript执行时间,同时在回调中主动处理重定向表单:

void AsiteSSOManager::OnLoginToRPLoadFinished(bool ok)
{
    if (ok)
    {
        // 延迟1秒执行,等待JavaScript完成重定向逻辑
        QTimer::singleShot(1000, this, [this]() {
            m_view->page()->toHtml([this](const QString &html) {
                if (html.contains("SAMLResponse") || html.contains("aSessionId"))
                {
                    m_loginToRPResponse = html; 
                    emit RequiredHTMLReceived();
                }
                else if (html.contains("redirectToAdoddleHome"))
                {
                    // 主动触发重定向表单提交,推进认证流程
                    m_view->page()->runJavaScript("redirectToAdoddleHome();");
                }
                else
                {
                    // 处理其他异常,触发循环退出
                    emit RequiredHTMLReceived();
                }
            });
        });
    }
}

2. 彻底清理缓存与Cookie

使用临时独立Profile,避免缓存共享并主动清理:

QString AsiteSSOManager::CallLoginToRP(const QString& strSSOADFSURL, const QString& strSSOTargetURL)
{
    QString retValHTML = "";
    // 创建临时Profile,隔离缓存环境
    QWebEngineProfile* tempProfile = new QWebEngineProfile(this);
    tempProfile->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
    tempProfile->setHttpCacheType(QWebEngineProfile::NoCache);
    tempProfile->clearHttpCache(); // 主动清理HTTP缓存
    
    m_view = new QWebEngineView();
    m_view->setPage(new QWebEnginePage(tempProfile, m_view));
    m_view->showFullScreen();
    
    // 后续代码保持不变...
}

3. 完善事件循环超时逻辑

添加超时机制,避免无限阻塞:

QString AsiteSSOManager::CallLoginToRP(const QString& strSSOADFSURL, const QString& strSSOTargetURL)
{
    // ... 其他代码 ...
    
    m_timer.setSingleShot(true);
    m_timer.setInterval(30000); // 设置30秒超时
    connect(&m_timer, &QTimer::timeout, this, &AsiteSSOManager::OnTimeOut);
    m_timer.start();
    
    QEventLoop loop;
    connect(this, &AsiteSSOManager::RequiredHTMLReceived, &loop, &QEventLoop::quit);
    connect(&m_timer, &QTimer::timeout, &loop, &QEventLoop::quit); // 超时强制退出
    loop.exec();
    
    // ... 其他代码 ...
}

void AsiteSSOManager::OnTimeOut()
{
    emit RequiredHTMLReceived();
    qWarning() << "SSO登录超时";
}

4. 监听URL变化追踪认证流程

通过urlChanged信号追踪页面跳转,到达目标页面时再获取HTML:

QString AsiteSSOManager::CallLoginToRP(const QString& strSSOADFSURL, const QString& strSSOTargetURL)
{
    // ... 其他代码 ...
    
    connect(m_view->page(), &QWebEnginePage::urlChanged, this, [this, strSSOTargetURL](const QUrl& url) {
        // 检查是否跳转到目标RP页面
        if (url.toString().contains(strSSOTargetURL))
        {
            m_view->page()->toHtml([this](const QString& html) {
                m_loginToRPResponse = html;
                emit RequiredHTMLReceived();
            });
        }
    });
    
    // ... 其他代码 ...
}

5. 升级Qt版本(推荐)

将Qt版本升级至5.15.x或Qt 6.x,这些版本的WebEngine基于更新的Chromium内核,对现代身份认证流程的支持更完善,能从根源上减少兼容性问题。


内容的提问来源于stack exchange,提问作者hetal agrawal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:53:18