有没有办法在PyQt4中强制使用QNetworkAccessManager进行身份验证?

问题描述:

在我的实现中,似乎QNetworkAccessManager在成功验证后缓存用于特定URL的凭证。有没有办法在PyQt4中强制使用QNetworkAccessManager进行身份验证?

随后请求使用不同凭据会成功相同的URL,但仅仅是因为它使用了缓存的凭据,而不是新的(在authenticationRequired信号未发出)。

(Py)Qt4中是否有某种方式清除缓存或以其他方式强制更新缓存的凭据?我应该删除经理并构建一个新的?还是有什么我做错了?据我所知,Qt 5提供了一个clearAccessCache()方法,但是Qt 4没有。

这个小例子说明了这个问题:

import sys 
import json 
from PyQt4 import QtNetwork, QtGui, QtCore 

def show_reply_content(reply): 
    content = json.loads(str(reply.readAll())) 
    if 'authenticated' in content and content['authenticated']: 
     print 'authentication successful for user {}'.format(
      content['user']) 
     reply.manager().replies_authenticated += 1 

    # Quit when all replies are finished 
    reply.deleteLater() 
    reply.manager().replies_unfinished -= 1 
    if not reply.manager().replies_unfinished: 
     print 'number of successful authentications: {}'.format(
      reply.manager().replies_authenticated) 
     app.quit() 


def provide_credentials(reply, authenticator): 
    print 'Setting credentials for {}:\nusername: {}\n' \ 
      'password: {}\n\n'.format(reply.url(), 
            reply.credentials['username'], 
            reply.credentials['password']) 
    authenticator.setUser(reply.credentials['username']) 
    authenticator.setPassword(reply.credentials['password']) 

# Some initialization 
app = QtGui.QApplication(sys.argv) 
manager = QtNetwork.QNetworkAccessManager() 
manager.finished.connect(show_reply_content) 
manager.authenticationRequired.connect(provide_credentials) 
manager.replies_unfinished = 0 
manager.replies_authenticated = 0 

# Specify credentials 
all_credentials = [dict(username='aap', 
         password='njkrfnq'), 
        dict(username='noot', 
         password='asdfber')] 

# url authenticates successfully only for the specified credentials 
url = 'http://httpbin.org/basic-auth/{}/{}'.format(
    *all_credentials[1].values()) 

# Schedule requests 
replies = [] 
for credentials in all_credentials: 
    replies.append(
     manager.get(QtNetwork.QNetworkRequest(QtCore.QUrl(url)))) 

    # Add credentials as dynamic attribute 
    replies[-1].credentials = credentials 

    manager.replies_unfinished += 1 

# Start event loop 
app.exec_() 

只要有一个请求被成功验证,下一个是为好,尽管它不应该。

+0

你使用的是什么特定版本的Qt4?这个错误看起来很相似:[QTBUG-15566](https://bugreports.qt.io/browse/QTBUG-15566)。 – ekhumoro

+0

也有关:[QNetworkAccessManager - 如何执行身份验证?](http://thread.gmane.org/gmane.comp.lib.qt.user/3141) – ekhumoro

+0

不确定Qt版本,但我使用的是PyQt4 .11.4。我仍然需要查看该错误报告,但第二个链接指向'QNetworkRequest.AuthenticationReuseAttribute',这听起来很有希望。不幸的是,我的第一次尝试,使用'request.setAttribute(QNetworkRequest.AuthenticationReuseAttribute,QNetworkRequest.Manual)',不起作用:现在所有的请求都被401拒绝。我稍后会介绍这一点。 – Dennis

事实证明也有几个类似的Qt的bug报告仍然打开:QTBUG-16835QTBUG-30433 ...

我想这应该是可以使用QNetworkRequest.AuthenticationReuseAttribute,但我不能让与多个请求的工作与具有不同(有效)证书集的相同url。

可能的解决方法是忘记所有关于authenticationRequired信号/插槽,并向每个请求添加原始标头(RFC 7617),确切地说是as described here

在这将意味着去除authenticationRequired信号/槽连接,并通过下面的线路(对于Python 2.7使用PyQt4的)替换manager.get()部分的例子:

request = QtNetwork.QNetworkRequest(QtCore.QUrl(url)) 
header_data = QtCore.QByteArray('{}:{}'.format(
    credentials['username'], credentials['password'])).toBase64() 
request.setRawHeader('Authorization', 'Basic {}'.format(header_data)) 
replies.append(manager.get(request)) 

然而,如@ekhumoro在指出上面的评论:不知道这是多么安全。