闲来没事,再来一篇博文,讲讲我最近做的QT实现的Webdav通信HTTPS实现。
HTTP太不安全了,抓包随便就可以拿到报文,但是Webdav有不能通过QT自带的authenticationRequired进行用户名和密码的验证,只好自己拼接报文头,
本项目就是介绍如何用HTTPS进行Webdav通信。
authenticationRequired就是相当于我们访问一个ftp,ftp需要用户输入用户名和密码,authenticationRequired就是让用户输入
如果url是http的话,authenticationRequired是有效的,可以直接用,但是HTTPS就不行,直接就是报错RemoteHostClosedError,所以自己实现了填写HTTPS头部
QString concatenated = QString("app1") + ":" + QString("ssssss");
QByteArray data = concatenated.toLocal8Bit().toBase64();
QString headerData = "Basic " + data;
gRequest.setRawHeader("Authorization", headerData.toLocal8Bit());
这样就行了,抓包看到的内容全部是加密的。安全了。
HTTP太不安全了,抓包随便就可以拿到报文。不信你看下图:
这个是我用http实现的webdav的访问抓包图:
首先是TCP的三次握手,接着客户端向服务器端发送了请求,明文的请求啊。后面从服务器端返回来的数据也是明文的。好的,直接上代码吧。
main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
webdavthreadclient = new webdavthread();
webdavthreadclient->DoSetup(cthread);
webdavthreadclient->moveToThread(&cthread);
connect(this, SIGNAL(startwebdav()), webdavthreadclient, SLOT(webdav()));
if(!cthread.isRunning()){
cthread.start();
}else{
emit startwebdav();
}
}
MainWindow::~MainWindow()
{
if(cthread.isRunning()){
cthread.quit();
cthread.wait();
}
delete ui;
}
webdavthread.cpp
#include "webdavthread.h"
#include
#include
#include
webdavthread::webdavthread(QObject *parent) : QObject(parent)
{
}
void webdavthread::DoSetup(QThread &cThread){
connect(&cThread,SIGNAL(started()),this,SLOT(webdav()));
}
void webdavthread::webdav(){
QNetworkAccessManager *gManager = new QNetworkAccessManager;
// Read the SSL certificate
QFile file("D:/QT_workspace/test01/certificate/ssl.crt");
//QFile file("D:/QT_workspace/QThreadWebdav/cert/localhost.pem");
//QFile file("D:/QT_workspace/QThreadWebdav/cert/localhost.crt");
file.open(QIODevice::ReadOnly);
QByteArray bytes = file.readAll();
// Create a certificate object
//QSslCertificate certificate(bytes);
// Add this certificate to all SSL connections
//QSslSocket::addDefaultCaCertificate(certificate);
QNetworkRequest gRequest;
//QSslConfiguration config = gRequest.sslConfiguration();
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
//config.setCaCertificates(certificate);
//config.setLocalCertificate(certificate);
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1_0OrLater);
//config.setLocalCertificate(certificate);
gRequest.setSslConfiguration(config);
QString url_s = "https://localhost/webdav.php";
QUrl url(url_s);
gRequest.setUrl(url);
gRequest.setRawHeader("Connection", "keep-alive");
QString concatenated = QString("username") + ":" + QString("password");
QByteArray data = concatenated.toLocal8Bit().toBase64();
QString headerData = "Basic " + data;
gRequest.setRawHeader("Authorization", headerData.toLocal8Bit());//这里是webdav的用户名和密码验证
gRequest.setRawHeader("Accept", "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,*\/*;q=0.8");
gRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
connect(gManager, SIGNAL(finished(QNetworkReply*)),this, SLOT(sendReplyFinished(QNetworkReply*)));
connect(gManager, SIGNAL(sslErrors(QNetworkReply*,QList)), this, SLOT(sslErrors(QNetworkReply*,QList)));
connect(gManager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(provideAuthenication(QNetworkReply*,QAuthenticator*)));
webdavthread::PropNames query;
QStringList props;
props << "getlastmodified";
props << "getcontentlength";
props << "resourcetype";
query["DAV:"] = props;
QByteArray query1;
query1 = "";
query1 += "";
query1 += "";
foreach (QString ns, query.keys())
{
foreach (const QString key, query[ns])
if (ns == "DAV:")
query1 += " ";
else
query1 += "<" + key + " xmlns=\"" + ns + "\"/>";
}
query1 += " ";
query1 += " ";
url.setPath("/");
//req.setUrl(reqUrl);
gRequest.setRawHeader("Depth", 1 == 2 ? QString("infinity").toUtf8() : QString::number(1).toUtf8());
QBuffer* dataIO = new QBuffer;
dataIO->setData(query1);
dataIO->open(QIODevice::ReadOnly);
if(dataIO != 0 && dataIO->size() !=0) {
gRequest.setHeader(QNetworkRequest::ContentLengthHeader, dataIO->size());
qDebug()<<"query1 is "<size();
//gRequest.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml; charset=utf-8");
}
// QString strdata("PROPFIND");
//QByteArray PROPData = QByteArray("PROPFIND", 9);
m_reply = gManager->sendCustomRequest(gRequest, "PROPFIND", dataIO);
connect(m_reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
}
void webdavthread::provideAuthenication(QNetworkReply* reply, QAuthenticator* authenticator){
qDebug() <<"\n"<<"in provideAuthenication";
if (reply == m_authenticator_lastReply) {
reply->abort();
//emit errorChanged("WebDAV server requires authentication. Check WebDAV share settings!");
reply->deleteLater();
reply=0;
}
m_authenticator_lastReply = reply;
QVariantHash opts = authenticator->options();
QVariant optVar;
foreach(optVar, opts) {
qDebug() << "QWebdav::authenticationRequired() option == " << optVar.toString();
}
authenticator->setUser(QString("username"));
authenticator->setPassword(QString("password"));
return;
}
void webdavthread::sendReplyFinished(QNetworkReply *re)
{
qDebug() <<"\n"<<"in sendReplyFinished";
qDebug() <error()<<"\n";
if(re->error() == QNetworkReply::NoError){
QString ans = re->readAll();
qDebug() <error() == QNetworkReply::RemoteHostClosedError){//RemoteHostClosedError为远处主机关闭了连接时发出的错误信号
qDebug() <error()<<"\n";
}
else if(re->error() == QNetworkReply::AuthenticationRequiredError){
}
}
void webdavthread::sslErrors(QNetworkReply *reply, const QList &errors)
{
qDebug() << "sslErrors() reply->url == " << reply->url().toString(QUrl::RemoveUserInfo);
qDebug() << "error is " << errors[0];
QSslCertificate sslcert = errors[0].certificate();
reply->ignoreSslErrors();
// if ( ( sslcert.digest(QCryptographicHash::Md5) == m_sslCertDigestMd5 ) &&
// ( sslcert.digest(QCryptographicHash::Sha1) == m_sslCertDigestSha1) )
// {
// // user accepted this SSL certifcate already ==> ignore SSL errors
// reply->ignoreSslErrors();
// } else {
// // user has to check the SSL certificate and has to accept manually
// emit checkSslCertifcate(errors);
// reply->abort();
// }
}
void webdavthread::slotReadyRead(){
qDebug() << "slotReadyRead";
}
void webdavthread::slotError(QNetworkReply::NetworkError error){
qDebug() << "slotError error" << error;
}
in sendReplyFinished
QNetworkReply::NetworkError(NoError)
"\n/cnrcloud/files/webdav.php/ Wed, 08 Jul 2015 12:54:11 GMT HTTP/1.1 200 OK HTTP/1.1 404 Not Found /cnrcloud/files/webdav.php/2015/ Tue, 21 Jul 2015 05:20:27 GMT HTTP/1.1 200 OK HTTP/1.1 404 Not Found /cnrcloud/files/webdav.php/20150723_XJ_05.mp4/ Wed, 29 Jul 2015 07:22:10 GMT HTTP/1.1 200 OK HTTP/1.1 404 Not Found /cnrcloud/files/webdav.php/20150723_XJ_06.mp4/ Wed, 29 Jul 2015 09:52:47 GMT
成功获取到了服务器端所有的文件。
再用wireshark抓包,可以看到所有内容都加密了。
好的,最后要想回到http,只需要把请求的url改成HTTP就行了。
github代码: https://github.com/buptis073114/QThreadWebdav