Qt SSL:从 QML 发出的请求握手失败
Posted
技术标签:
【中文标题】Qt SSL:从 QML 发出的请求握手失败【英文标题】:Qt SSL: Handshake failed for requests made from QML 【发布时间】:2015-05-19 22:40:14 【问题描述】:我直接从我的 qml 视图发出一些 https 请求,例如图像源。由于我有一个自签名证书服务器端,我需要告诉 qt 忽略一些 ssl 错误(我同时控制服务器和客户端应用程序,所以这应该不是问题)。
我创建了一个 QQmlNetworkAccessManagerFactory 来创建 NAM,我在其中连接到 sslErrors 信号。
UltraQmlAccessManagerFactory.h:
#ifndef FACKFACKTORy_H
#define FACKFACKTORy_H
#include <QQmlNetworkAccessManagerFactory>
#include <QObject>
#include <QNetworkReply>
#include <QList>
#include <QSslError>
#include <QNetworkAccessManager>
#include <QDebug>
#include <QSslCertificate>
class UltraQmlNetworkAccessManagerFactory : public QObject,
public QQmlNetworkAccessManagerFactory
Q_OBJECT
private:
QNetworkAccessManager* nam;
QList<QSslError> expectedSslErrors;
public:
explicit UltraQmlNetworkAccessManagerFactory();
~UltraQmlNetworkAccessManagerFactory();
virtual QNetworkAccessManager* create(QObject* parent);
public slots:
void onIgnoreSslErrors(QNetworkReply* reply, QList<QSslError> errors);
;
#endif
UltraQmlNetworkAccessManagerFactory.cpp:
#include "UltraQmlNetworkAccessManagerFactory.h"
UltraQmlNetworkAccessManagerFactory::UltraQmlNetworkAccessManagerFactory()
UltraQmlNetworkAccessManagerFactory::~UltraQmlNetworkAccessManagerFactory()
delete nam;
QNetworkAccessManager* UltraQmlNetworkAccessManagerFactory::create(QObject* parent)
QNetworkAccessManager* nam = new QNetworkAccessManager(parent);
QObject::connect(nam, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)),
this, SLOT(onIgnoreSslErrors(QNetworkReply*,QList<QSslError>))
);
return nam;
void UltraQmlNetworkAccessManagerFactory::onIgnoreSslErrors(QNetworkReply *reply, QList<QSslError> errors)
for (int i = 0; i < errors.size(); i++)
qDebug() << "e: " << errors.at(i) << endl;
reply->ignoreSslErrors(errors);
main.cpp 中还有一些胶水可以设置这个工厂被使用,我怀疑这部分是错误的来源,因为 qDebug 打印在输出中是可见的。
从函数/槽 onIgnoreSslErrors 中的 .cpp 文件中可以看出,我尝试忽略收到的每个错误(作为测试),但在输出中我没有得到预期的结果。
输出
e: "The certificate is self-signed, and untrusted"
qrc:/qml/file/ImageView.qml:16:5: QML Image: SSL handshake failed
我已经成功地从 C++ 直接使用 QSslConfiguration
创建了 QNetworkRequests
,并指定了 TLSV1_0 和证书。由于我怀疑握手失败是因为一侧需要 SSL,而另一侧需要 TLS,因此我还尝试通过 QNetworkRequest
对象设置 QSslConfiguration
reply->request();
,但这并没有改变。
【问题讨论】:
尝试使用QSslConfiguration::setDefaultConfiguration()设置默认的SSL配置。 【参考方案1】:(这是一个非常古老的问题,但由于我最近偶然发现并没有找到答案,我认为它仍然值得回答)
您没有显示实际设置工厂对象的位置,但它很可能是工厂对象不属于调用其create()
方法时使用的同一(实际上是任何)线程。这是Qt documentation on the class的摘录:
如果要从 create() 返回的对象的信号连接到可能在不同线程中创建的对象的插槽,则开发人员应小心
它进一步提到了authenticationRequired()
信号,但sslErrors()
的行为方式相同:这两个信号以及其他一些信号都需要直接或阻塞队列连接,以便在返回到发出位置时表示网络回复对象已经由槽配置。
在您的情况下发生的情况(很可能)如下(TL;DR:您的插槽被排队连接异步调用,因为它位于不同的线程中,而sslErrors()
需要对正在运行的网络回复进行同步更改对象;不管日志行的顺序,请求首先失败,然后调用ignoreSslErrors()
):
-
工厂对象被创建,QML 引擎被配置,在主线程中。
QML 引擎产生一些线程来执行后端工作,特别是对 URL 的网络请求(我在这里假设您的
ImageView.qml
有一个 Image
组件)。为了执行网络请求,这些线程调用UltraQmlNetworkAccessManagerFactory::create()
。
create()
生成一个 NAM 对象并在其上建立连接。这里的父对象是 QQmlEngine 对象或(专门用于图像请求)后端线程对象,如您所见,例如here。因此,这个 NAM 对象属于后端线程。
connect()
默认使用Qt::AutoConnection
类型,由于工厂线程和NAM对象线程不同,对应Qt::QueuedConnection
。作为旁注,线程已检查at the time of signal invocation。
最终会发出QNetworkAccessManager::sslErrors()
信号。由于这是一个排队连接,唯一立即发生的事情就是在主线程的事件队列中调用onIgnoreSslErrors()
。
如果您非常幸运,您可能会在此之后立即切换到主线程 - 但实际上没有什么可以确保这一点,因此控制权更有可能返回到发出 QNetworkAccessManager::sslErrors()
的站点。由于没有调用ignoreSslErrors()
,因此请求握手失败。后端线程posts relevant data from the (failed) QNetworkReply
object back to the main thread - see the postReply
call in the middle of the method(或者它可能稍后再做——这已经不重要了)。
一旦上下文切换到主线程ignoreSslErrors()
被执行——唉,已经太晚了,因为网络回复很可能已经以失败告终;但现在第一条日志行出来了。
主线程继续通过事件循环并找到带有故障数据的QQuickPixmapReply::Event
对象。在展开一些调用和信号后,失败的图像最终出现在 QQuickImageBase::requestFinished()
that prints the second log line for you 中。
至于修复,很容易将Qt::BlockingQueuedConnection
指定为connect()
的第五个参数。不幸的是,如果曾经从在主线程中运行的QQmlEngine
发出请求,并且使用该工厂创建的 NAM 实例来例如通过网络请求 QML 组件,这将死锁。到目前为止,我能想到的最好的方法是
connect(nam, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)),
this, SLOT(onIgnoreSslErrors(QNetworkReply*,QList<QSslError>)),
currentThread() == this->thread() ? Qt::DirectConnection
: Qt::BlockingQueuedConnection
);
【讨论】:
以上是关于Qt SSL:从 QML 发出的请求握手失败的主要内容,如果未能解决你的问题,请参考以下文章
每日一题请你分别说明一下,SSL协议和TCP协议的4次握手过程