Qt中使用HTTPS
一、HTTPS和HTTP区别
1. 从定义上看
HTTP: HyperText Transfer Protocol
HTTPS: HyperText Transfer Protocol over Secure Socket Layer
2. 从分层上看
HTTP: HTTP -> Socket API -> TCP/QUIC①
HTTPS: HTTP -> 安全层 -> Socket API -> TCP/QUIC
3. 安全层
显而易见,HTTPS比HTTP多了一个“安全层②”。
所谓“安全层”,无非是为了保证数据安全。其中涉及的技术,简单来说有三个:
- 数据加密:防止数据被第三方窥探到。通过AES、DES、RSA等加解密算法实现
- 数据完整性:防止数据被破坏。通过各种散列函数算法实现,如MD4/MD5,SHA-1/SHA-256等
- 通信双方认证:防止冒充。通过证书技术实现
4. 安全层实现
安全层实现主流且常见的有OpenSSL、Mbed TLS,双方区别主要应用场景不同:
- OpenSSL庞大,主要应用于PC、高端CPU上,如支持Linux的CPU
- Mbed TLS更加轻量,可以在一些低端CPU,如Arm的Cortex-M系列上运行。在IoT领域,Mbed TLS大放异彩
二、Qt HTTPS环境配置
Qt的安全层使用的是OpenSSL,支持HTTPS请求需要配置OpenSSL环境。
不过,无需自己编译OpenSSL或者满世界找编译好的库。Qt的安装路径下已经有现成的dll库。
以Mingw编译环境为例,这两个dll位于:C:\Qt\Qt5.9.1\Tools\mingw530_32\opt\bin。
把libeay32.dll 和 ssleay32.dll拷贝到程序生成目录下(即生成exe的同级目录)或者加入到系统环境变量里都可以。
三、HTTPS代码示例
1. 准备工作
- HTTPS服务器:https://httpbin.org
- 调试工具:curl
2.示例功能
- 向HTTPS服务器发送POST方法,获取Json格式数据,显示在一个QPlainText控件里
- 向HTTPS服务器发送GET方法,获取一个PNG格式图片,显示在一个QLabel控件里
3.UI和数据分离
HttpClient负责HTTPS网络连接,数据获取;获取数据后发送Signal给HttpClientView,HttpClientView负责数据结果呈现。
4.示例代码
4.1 HttpClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | ---HttpClient.h #ifndef HTTPSCLIENT_H #define HTTPSCLIENT_H #include <QString> #include <QByteArray> #include <QNetworkAccessManager> #include <QNetworkReply> class HttpClient : public QObject { Q_OBJECT public : enum HttpMethod{ POST = 0, GET, DELETE, PUT }; Q_ENUM(HttpMethod) HttpClient(); ~HttpClient(); void doRequest(HttpClient::HttpMethod method, QString& url); private slots: void postFinishSlot(); void httpErrorSlot(QNetworkReply::NetworkError err); signals: void postFinish( int statusCode, QByteArray& response); void httpError(QNetworkReply::NetworkError err); private : QNetworkAccessManager *mAccessManager; QNetworkReply *mReply; }; #endif // HTTPSCLIENT_H ---HttpClient.cpp #include <QSsl> #include <QUrl> #include <QSslSocket> #include <QSslConfiguration> #include <QNetworkRequest> #include <QNetworkReply> #include <QVariant> #define LOG_TAG "HttpClient" #include "../log/log.h" #include "HttpClient.h" HttpClient::HttpClient() : mAccessManager(NULL) { log_debug( "NetworkAccessManager init..." ); mAccessManager = new QNetworkAccessManager( this ); log_debug( "NetworkAccessManager init done." ); } HttpClient::~HttpClient() { log_info( "desc" ); if (mAccessManager) { delete mAccessManager; mAccessManager = NULL; } } void HttpClient::doRequest(HttpClient::HttpMethod method, QString &url) { log_info( "do request..." ); QNetworkRequest request; QSslConfiguration cfg = request.sslConfiguration(); cfg.setPeerVerifyMode(QSslSocket::VerifyNone); cfg.setProtocol(QSsl::AnyProtocol); request.setSslConfiguration(cfg); request.setUrl(QUrl(url)); if (method == POST) { request.setRawHeader( "Content-Type: " , "application/json;charset=utf-8" ); request.setRawHeader( "Accept" , "application/json" ); QByteArray data; data.clear(); mReply = mAccessManager->post(request, data); } else if (method == GET) { request.setRawHeader( "Content-Type: " , "image/png" ); request.setRawHeader( "Accept" , "image/png" ); mReply = mAccessManager->get(request); } connect(mReply, SIGNAL(finished()), this , SLOT(postFinishSlot())); connect(mReply, SIGNAL(error(QNetworkReply::NetworkError)), this , SLOT(httpErrorSlot(QNetworkReply::NetworkError))); } void HttpClient::postFinishSlot() { QVariant statusCode = mReply->attribute(QNetworkRequest::HttpStatusCodeAttribute); int code = statusCode.toInt(); QByteArray resp = mReply->readAll(); QList<QByteArray> hdrNames = mReply->rawHeaderList(); log_info( "response headers:" ); for ( int i = 0; i < hdrNames.size(); i ++) { QByteArray hdrName = hdrNames.at(i); QByteArray hdrCtx = mReply->rawHeader(hdrName); log_debug( "%s: %s" , hdrName.constData(), hdrCtx.constData()); } emit postFinish(code, resp); } void HttpClient::httpErrorSlot(QNetworkReply::NetworkError err) { emit httpError(err); } |
4.2 HttpClientView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | ---HttpClientView.h #ifndef HTTPCLIENTVIEW_H #define HTTPCLIENTVIEW_H #include <QWidget> #include "HttpClient.h" namespace Ui { class HttpClientView; } class HttpClientView : public QWidget { Q_OBJECT public : explicit HttpClientView(QWidget *parent = 0); ~HttpClientView(); private : void init(); void initUI(); void initSlot(); private slots: void onBtnExecuteClicked(); void postFinishSlot( int statusCode, QByteArray& response); void httpErrorSlot(QNetworkReply::NetworkError err); private : Ui::HttpClientView *ui; HttpClient *mHttpClient; }; #endif // HTTPCLIENTVIEW_H ---HttpClientView.cpp #define LOG_TAG "HttpView" #include "../log/log.h" #include <QImage> #include <QPixmap> #include "HttpClientView.h" #include "ui_httpclientview.h" HttpClientView::HttpClientView(QWidget *parent) : QWidget(parent), mHttpClient(NULL), ui( new Ui::HttpClientView) { ui->setupUi( this ); init(); initUI(); initSlot(); } HttpClientView::~HttpClientView() { if (mHttpClient) { delete mHttpClient; mHttpClient = NULL; } delete ui; } void HttpClientView::init() { mHttpClient = new HttpClient(); connect(mHttpClient, SIGNAL(postFinish( int ,QByteArray&)), this , SLOT(postFinishSlot( int ,QByteArray&))); connect(mHttpClient, SIGNAL(httpError(QNetworkReply::NetworkError)), this , SLOT(httpErrorSlot(QNetworkReply::NetworkError))); } void HttpClientView::initUI() { ui->labelImage->setStyleSheet( "{border:2px dotted #242424;}" ); } void HttpClientView::initSlot() { connect(ui->btnExecute, SIGNAL(clicked()), this , SLOT(onBtnExecuteClicked())); } void HttpClientView::onBtnExecuteClicked() { int scheme = ui->cBoxScheme->currentIndex(); int method = ui->cBoxMethod->currentIndex(); ui->plainTextEdit->appendPlainText( "request..." ); log_info( "http scheme %d, method %d" , scheme, method); if (0 == method) { QString url( "https://httpbin.org/post" ); mHttpClient->doRequest(HttpClient::POST, url); } else if (1 == method) { QString url( "https://httpbin.org/image/png" ); mHttpClient->doRequest(HttpClient::GET, url); } } void HttpClientView::postFinishSlot( int statusCode, QByteArray& response) { int size = response.size(); ui->plainTextEdit->appendPlainText( "request finish." ); log_info( "response code: %d, data size: %d" , statusCode, size); if (size > 0) { if (0 == ui->cBoxMethod->currentIndex()) { ui->plainTextEdit->appendPlainText(QString(response)); } else if (1 == ui->cBoxMethod->currentIndex()) { QImage img; bool res = img.loadFromData(response); if (res) { ui->labelImage->setAlignment(Qt::AlignCenter); ui->labelImage->setPixmap(QPixmap::fromImage(img)); } else { log_error( "error convert image from http response data!" ); } } } } void HttpClientView::httpErrorSlot(QNetworkReply::NetworkError err) { log_error( "http error: %d\r\n" , err); } |
4.3 UI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | <? xml version="1.0" encoding="UTF-8"?> < ui version="4.0"> < class >HttpClientView</ class > < widget class="QWidget" name="HttpClientView"> < property name="geometry"> < rect > < x >0</ x > < y >0</ y > < width >634</ width > < height >490</ height > </ rect > </ property > < property name="font"> < font > < pointsize >11</ pointsize > < weight >75</ weight > < bold >true</ bold > </ font > </ property > < property name="windowTitle"> < string >HttpClientView</ string > </ property > < layout class="QGridLayout" name="gridLayout"> < item row="0" column="0" rowspan="4"> < widget class="QPlainTextEdit" name="plainTextEdit"> < property name="minimumSize"> < size > < width >370</ width > < height >0</ height > </ size > </ property > < property name="font"> < font > < pointsize >11</ pointsize > < weight >50</ weight > < bold >false</ bold > </ font > </ property > </ widget > </ item > < item row="0" column="1" colspan="4"> < widget class="QLabel" name="labelImage"> < property name="enabled"> < bool >true</ bool > </ property > < property name="minimumSize"> < size > < width >240</ width > < height >240</ height > </ size > </ property > < property name="maximumSize"> < size > < width >16777215</ width > < height >16777215</ height > </ size > </ property > < property name="font"> < font > < weight >50</ weight > < bold >false</ bold > </ font > </ property > < property name="styleSheet"> < string notr="true">border:2px dotted #242424;</ string > </ property > < property name="text"> < string /> </ property > </ widget > </ item > < item row="1" column="1"> < spacer name="verticalSpacer_2"> < property name="orientation"> < enum >Qt::Vertical</ enum > </ property > < property name="sizeHint" stdset="0"> < size > < width >20</ width > < height >144</ height > </ size > </ property > </ spacer > </ item > < item row="1" column="3"> < spacer name="verticalSpacer"> < property name="orientation"> < enum >Qt::Vertical</ enum > </ property > < property name="sizeHint" stdset="0"> < size > < width >20</ width > < height >144</ height > </ size > </ property > </ spacer > </ item > < item row="2" column="1"> < layout class="QVBoxLayout" name="verticalLayout"> < item > < widget class="QLabel" name="labelScheme"> < property name="enabled"> < bool >true</ bool > </ property > < property name="text"> < string >Schemes</ string > </ property > </ widget > </ item > < item > < widget class="QComboBox" name="cBoxScheme"> < property name="font"> < font > < weight >50</ weight > < bold >false</ bold > </ font > </ property > < item > < property name="text"> < string >HTTPS</ string > </ property > </ item > < item > < property name="text"> < string >HTTP</ string > </ property > </ item > </ widget > </ item > </ layout > </ item > < item row="2" column="2"> < spacer name="horizontalSpacer"> < property name="orientation"> < enum >Qt::Horizontal</ enum > </ property > < property name="sizeHint" stdset="0"> < size > < width >8</ width > < height >20</ height > </ size > </ property > </ spacer > </ item > < item row="2" column="3"> < layout class="QVBoxLayout" name="verticalLayout_2"> < item > < widget class="QLabel" name="labelHttp"> < property name="enabled"> < bool >true</ bool > </ property > < property name="text"> < string >Methods</ string > </ property > </ widget > </ item > < item > < widget class="QComboBox" name="cBoxMethod"> < property name="font"> < font > < weight >50</ weight > < bold >false</ bold > </ font > </ property > < item > < property name="text"> < string >POST</ string > </ property > </ item > < item > < property name="text"> < string >GET</ string > </ property > </ item > < item > < property name="text"> < string >DELETE</ string > </ property > </ item > < item > < property name="text"> < string >PUT</ string > </ property > </ item > </ widget > </ item > </ layout > </ item > < item row="2" column="4"> < spacer name="horizontalSpacer_2"> < property name="orientation"> < enum >Qt::Horizontal</ enum > </ property > < property name="sizeHint" stdset="0"> < size > < width >64</ width > < height >20</ height > </ size > </ property > </ spacer > </ item > < item row="3" column="1" colspan="4"> < widget class="QPushButton" name="btnExecute"> < property name="minimumSize"> < size > < width >240</ width > < height >0</ height > </ size > </ property > < property name="font"> < font > < pointsize >11</ pointsize > </ font > </ property > < property name="styleSheet"> < string notr="true">background-color: rgb(73, 144, 226); color: rgb(255, 255, 255);</ string > </ property > < property name="text"> < string >执行</ string > </ property > </ widget > </ item > </ layout > < zorder >layoutWidget</ zorder > < zorder >layoutWidget</ zorder > < zorder >plainTextEdit</ zorder > < zorder >labelImage</ zorder > < zorder >btnExecute</ zorder > < zorder >horizontalSpacer</ zorder > < zorder >verticalSpacer</ zorder > < zorder >horizontalSpacer_2</ zorder > < zorder >verticalSpacer_2</ zorder > </ widget > < resources /> < connections /> </ ui > |
4.4 关键代码
四、报错、原因分析及解决
1. qt.network.ssl: QSslSocket: cannot call unresolved function
原因:未找到libeay32.dll 和 ssleay32.dll,检查环境设置。
2.QNetworkReply::UnknownNetworkError
原因:由问题1引起,解决方式同上。
五、附录
①QUIC:QUIC协议原理分析
②安全相关技术参阅《图解密码技术》
2021.10.28
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战