Qt身份验证需要通过HTTPS和下载文件

时间:2016-01-28 23:23:17

标签: c++ qt authentication https

我一直在尝试调整一个用于从服务器下载单个文件的小型Qt程序。所有它(现在)都要求提供一个URL,然后下载它。

在进行更复杂的操作之前,我想要做的是让它与身份验证一起使用。在阅读文档并找到一些涉及QNetworkAccessManager和QAuthenticator类的示例后,我尝试修改它。但由于某种原因,当我正确地将来自我的QNetworkAccessManager实例的authenticationRequired信号链接到自定义插槽并将用户名/密码设置为QAuthenticator对象时,似乎永远不会发送authenticationRequired信号(将qDebug放入插槽中。用户名和密码现在是硬编码的(我计划稍后在我开始工作时加入)。

我验证了我在调用get()之前放了connect()并尝试了几个地方:我尝试的第一件事就是将QNetworkAccessManager创建放在我的类'构造函数中,然后才使用get()。但似乎没有任何工作,因为我在尝试下载需要auth时获得的是“禁止”错误代码。因为我之前从未使用过Qt Network课程,所以我不确定在哪里发现我做错了什么。

这是我的完整代码

//HttpDownload.h
#ifndef HTTPDOWNLOAD_H
#define HTTPDOWNLOAD_H

#include <QDialog>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QProgressDialog>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QMessageBox>
#include <QDebug>
#include <QAuthenticator>

namespace Ui {
class HttpDownload;
}

class HttpDownload : public QDialog
{
    Q_OBJECT

public:
    explicit HttpDownload(QWidget *parent = 0);
    ~HttpDownload();

public:
    void startRequest(QUrl url);
private slots:
    void on_downloadButton_clicked();

    void on_quitButton_clicked();

    void on_urlEdit_returnPressed();

    // slot for readyRead() signal
    void httpReadyRead();

    // slot for finished() signal from reply
    void httpDownloadFinished();

    // slot for downloadProgress()
    void updateDownloadProgress(qint64, qint64);

    void enableDownloadButton();
    void cancelDownload();
    void authRequired(QNetworkReply*, QAuthenticator*); // Auth slot
private:
    Ui::HttpDownload *ui;
    QUrl url;
    QNetworkAccessManager *manager;
    QNetworkReply *reply;
    QProgressDialog *progressDialog;
    QFile *file;
    bool httpRequestAborted;
    qint64 fileSize;

};

#endif // HTTPDOWNLOAD_H

//HttpDownload.cpp
#include "httpdownload.h"
#include "ui_httpdownload.h"

HttpDownload::HttpDownload(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::HttpDownload)
{
    ui->setupUi(this);
    ui->urlEdit->setText(HTTPS_URL);
    ui->statusLabel->setWordWrap(true);
    ui->downloadButton->setDefault(true);
    ui->quitButton->setAutoDefault(false);

    progressDialog = new QProgressDialog(this);

    connect(ui->urlEdit,
            SIGNAL(textChanged(QString)),
            this,
            SLOT(enableDownloadButton())
        );
    connect(progressDialog,
            SIGNAL(canceled()),
            this,
            SLOT(cancelDownload())
        );
}

HttpDownload::~HttpDownload()
{
    delete ui;
}

// Auth slot
void HttpDownload::authRequired(QNetworkReply *reply, QAuthenticator *ator)
{
    qDebug() << "auth Alert";
    qDebug() << reply->readAll(); // this is just to see what we received
    qDebug() << Q_FUNC_INFO << ator->realm();
    ator->setUser(QString(USERNAME));
    ator->setPassword(QString(PASSWORD));
}

void HttpDownload::on_downloadButton_clicked()
{
//    manager = new QNetworkAccessManager(this);

    // get url
    url = (ui->urlEdit->text());

    QFileInfo fileInfo(url.path());
    QString fileName = fileInfo.fileName();

    if (fileName.isEmpty())
        fileName = "index.html";

    if (QFile::exists(fileName)) {
        if (QMessageBox::question(this, tr("HTTP"),
                tr("There already exists a file called %1 in "
                "the current directory. Overwrite?").arg(fileName),
                QMessageBox::Yes|QMessageBox::No, QMessageBox::No)
                == QMessageBox::No)
                return;
        QFile::remove(fileName);
    }

    file = new QFile(fileName);
    if (!file->open(QIODevice::WriteOnly)) {
        QMessageBox::information(this, tr("HTTP"),
                      tr("Unable to save the file %1: %2.")
                      .arg(fileName).arg(file->errorString()));
        delete file;
        file = 0;
        return;
    }

    // used for progressDialog
    // This will be set true when canceled from progress dialog
    httpRequestAborted = false;

    progressDialog->setWindowTitle(tr("HTTP"));
    progressDialog->setLabelText(tr("Downloading %1.").arg(fileName));

    // download button disabled after requesting download
    ui->downloadButton->setEnabled(false);

    startRequest(url);
}

void HttpDownload::httpReadyRead()
{
    // this slot gets called every time the QNetworkReply has new data.
    // We read all of its new data and write it into the file.
    // That way we use less RAM than when reading it at the finished()
    // signal of the QNetworkReply
    if (file)
        file->write(reply->readAll());
}

void HttpDownload::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes)
{
    if (httpRequestAborted)
        return;

    progressDialog->setMaximum(totalBytes);
    progressDialog->setValue(bytesRead);
}

void HttpDownload::on_quitButton_clicked()
{
    this->close();
}

void HttpDownload::on_urlEdit_returnPressed()
{
    on_downloadButton_clicked();
}

void HttpDownload::enableDownloadButton()
{
    ui->downloadButton->setEnabled(!(ui->urlEdit->text()).isEmpty());
}

// During the download progress, it can be canceled
void HttpDownload::cancelDownload()
{
    ui->statusLabel->setText(tr("Download canceled."));
    httpRequestAborted = true;
    reply->abort();
    ui->downloadButton->setEnabled(true);
}

// When download finished or canceled, this will be called
void HttpDownload::httpDownloadFinished()
{
    // when canceled
    if (httpRequestAborted) {
        if (file) {
            file->close();
            file->remove();
            delete file;
            file = 0;
        }
        reply->deleteLater();
        progressDialog->hide();
        return;
    }

    // download finished normally
    progressDialog->hide();
    file->flush();
    file->close();

    // get redirection url
    QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if (reply->error()) {
        file->remove();
        QMessageBox::information(this, tr("HTTP"),
                                 tr("Download failed: %1.")
                                 .arg(reply->errorString()));
        ui->downloadButton->setEnabled(true);
    } else if (!redirectionTarget.isNull()) {
        QUrl newUrl = url.resolved(redirectionTarget.toUrl());
        if (QMessageBox::question(this, tr("HTTP"),
                                  tr("Redirect to %1 ?").arg(newUrl.toString()),
                                  QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
            url = newUrl;
            reply->deleteLater();
            file->open(QIODevice::WriteOnly);
            file->resize(0);
            startRequest(url);
            return;
        }
    } else {
        QString fileName = QFileInfo(QUrl(ui->urlEdit->text()).path()).fileName();
        ui->statusLabel->setText(tr("Downloaded %1 to %2.").arg(fileName).arg(QDir::currentPath()));
        ui->downloadButton->setEnabled(true);
    }

    reply->deleteLater();
    reply = 0;
    delete file;
    file = 0;
    manager = 0;
}

// This will be called when download button is clicked
void HttpDownload::startRequest(QUrl url)
{
    qDebug() << "Begin download";

    manager = new QNetworkAccessManager(this);

    connect(manager,
        SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
        SLOT(authRequired(QNetworkReply*,QAuthenticator*))
        );

    // get() method posts a request
    // to obtain the contents of the target request
    // and returns a new QNetworkReply object
    // opened for reading which emits
    // the readyRead() signal whenever new data arrives.
    reply = manager->get(QNetworkRequest(url));

    // Whenever more data is received from the network,
    // this readyRead() signal is emitted
    connect(reply, SIGNAL(readyRead()),
            this, SLOT(httpReadyRead()));

    // Also, downloadProgress() signal is emitted when data is received
    connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
            this, SLOT(updateDownloadProgress(qint64,qint64)));

    // This signal is emitted when the reply has finished processing.
    // After this signal is emitted,
    // there will be no more updates to the reply's data or metadata.
    connect(reply, SIGNAL(finished()),
            this, SLOT(httpDownloadFinished()));
}

//main.cpp
#include "httpdownload.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    HttpDownload w;
    w.setWindowTitle("Http Download");
    w.show();

    return a.exec();
}

2 个答案:

答案 0 :(得分:-1)

连接将来会发生的接听电话。在任何可能触发它们的调用之前,您应该完成所有连接。之后,如果auth事件永远不会发生,则表示主机是问题,而不是代码。您的auth方法看起来是正确的。尝试使用Fiddler或类似的东西进行追踪。

答案 1 :(得分:-1)

我认为您在SLOT之前错过了 this

connect(manager,
        SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
        SLOT(authRequired(QNetworkReply*,QAuthenticator*))
        );

应该是

connect(manager,
            SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
            this, SLOT(authRequired(QNetworkReply*,QAuthenticator*))
            );