周星星 阅读(3687) 评论(4)

第一次用Qt,想用Qt下载个文件,因为公司使用最差的代理服务器ISA上网,用尽Demo中的代码和网上Google到的代码,皆不成功。
下载了个查看网络数据包的软件Microsoft Network Monitor,发现浏览器用的是Negotiate认证,Qt用的是NTLM认证。
在源代码中搜索"negotiate",找是找到了,但发现其代码没有被任何地方调用。
同时,在源代码中有三个函数引起我的注意,调式了一下,发现调用过程如下:
void QAuthenticator::setUser(const QString &user)
{
    ……
    switch(d->method) { // d->method的值一直是 QAuthenticatorPrivate::None
    case QAuthenticatorPrivate::Ntlm: // 也就是说永远不执行这条语句
        if((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) {
            d->realm.clear();
            d->userDomain  = user.left(separatorPosn);
            d->extractedUser = user.mid(separatorPosn + 1);
            d->user = user;
        }
 ……
    }
}
此函数调用6次后调用
QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
{
    …… // 执行完毕后method设为正确的 QAuthenticatorPrivate::Ntlm
}
此函数执行完毕后到达
void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy)
{
    ……
    case Ntlm:
        // #### extract from header // 哇靠,什么都没写,是不需要呢,还是忘了写?
        break;
}
整个过程简而言之为:如果是Ntlm认证,应该将形如 XXXdomain/YYYuser 的用户名分解成 XXXdomain主域 和 YYYuser用户名 两部分,但设置用户名之前它不知道是Ntlm认证,知道是Ntlm认证后又不再设置用户名。
粗略查了一下源代码,除了修改源代码之外,不知道怎么优美的解开这个死结。

以下为我写的一个demo,解决方式有点丑。第一次使用Qt,有错误或用法不正宗的地方请指教
httpget.h文件
#ifndef HTTPGET_H
#define HTTPGET_H

#include <QObject>
#include <QUrl>
#include <QNetworkProxy>
#include <QAuthenticator>
#include <QNetworkAccessManager>
#include <QFile>

class HttpGet : public QObject
{
    Q_OBJECT

public:
    HttpGet( QObject* parent=0 );
    ~HttpGet();

    void SetProxy( const QString& host, int port, const QString& username=QString(), const QString& password=QString() );
    bool DownloadFile( const QUrl& url, const QString& filename );

signals:
    void done();

private slots:
    void finished_( QNetworkReply* reply );
    void proxyAuthenticationRequired_( const QNetworkProxy& proxy, QAuthenticator* authenticator );
    void authenticationRequired_( QNetworkReply* reply, QAuthenticator* authenticator);

private:
    QString authenticator_username_, authenticator_password_;
    QNetworkAccessManager manager_;
    QFile file_;
};

#endif // HTTPGET_H

httpget.cpp文件
##include "httpget.h"
#include <QNetworkReply>

HttpGet::HttpGet(QObject *parent)
    : QObject(parent), manager_(this)
{
    connect( &manager_, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&,QAuthenticator*))
        , this, SLOT(proxyAuthenticationRequired_(const QNetworkProxy&,QAuthenticator*)) );
    connect( &manager_, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))
        , this, SLOT(authenticationRequired_(QNetworkReply*,QAuthenticator*)) );
    connect( &manager_, SIGNAL(finished(QNetworkReply*))
        , this, SLOT(finished_(QNetworkReply*)) );
}

HttpGet::~HttpGet()
{
}

void HttpGet::SetProxy( const QString& host, int port, const QString& username, const QString& password )
{
    QNetworkProxy proxy;
    proxy.setType( QNetworkProxy::HttpProxy );
    proxy.setHostName( host );
    proxy.setPort( port );

    // 不要在此设置用户名和密码,一来无用,二者会导致找不到Cached Proxy Credentials,以致每调用一次DownloadFile则需调用一次proxyAuthenticationRequired
    // proxy.setUser( username );
    // proxy.setPassword( password );

    authenticator_username_ = username;
    authenticator_password_ = password;

    manager_.setProxy( proxy );
}

bool HttpGet::DownloadFile( const QUrl& url, const QString& filename )
{
    file_.setFileName( filename );
    if( !file_.open(QIODevice::WriteOnly) )
        return false;

    manager_.get( QNetworkRequest(url) );
    return true;
}

void HttpGet::proxyAuthenticationRequired_( const QNetworkProxy& proxy, QAuthenticator* authenticator )
{
    authenticator->setUser( authenticator_username_ ); // 此时已经知道正确的代理认证名Ntlm,因此此时设置用户名会做正确的处理
    authenticator->setPassword( authenticator_password_ );
}
void HttpGet::authenticationRequired_( QNetworkReply* reply, QAuthenticator* authenticator)
{
    return;
}

//#include <QTextCodec>
void HttpGet::finished_( QNetworkReply* reply )
{
    //QTextCodec* codec = QTextCodec::codecForName("utf8");
    //QString all = codec->toUnicode(reply->readAll());
    //file_.write( all.toAscii() );
    file_.write( reply->readAll() );
    reply->deleteLater();
    file_.close();

    emit done();
}

main.cpp文件
#include "httpget.h"
#include <QtCore/QCoreApplication>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    HttpGet getter;
    getter.SetProxy( "代理服务器地址", 端口, "域\\用户名", "密码" );
    getter.DownloadFile( QUrl("http://www.okbase.net"), "vckbase.html" );

    QObject::connect( &getter, SIGNAL(done()), &app, SLOT(quit()) );

    return app.exec();
}


评论列表
hATEmATH的网上田园
re: Qt 4.7.3 之 代理认证
我代表广大bloger要求:赶紧把qt写的vckbase blog备份软件发布出来,急用哇。
周星星
re hatemath:
qt 的库很大的,QtCore4.dll要2M多,QtGui4.dll要8M,……
如果我给你源代码,你要安装VS2008,安装Qt libraries 4.7.4 for Windows (VS 2008, 228 MB),以及 Qt Visual Studio Add-in(does not work with the Express edition)

但是,如果你只是想备份自己的blog,我可以帮你备份一下,在bbs中给我留言,给出你的mail,然后我打包发给你

发表评论
切换编辑模式