___2017

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  326 随笔 :: 18 文章 :: 15 评论 :: 18万 阅读

 


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. 安全层实现

安全层实现主流且常见的有OpenSSLMbed 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

posted on   yin'xiang  阅读(6145)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示