• Qt中使用HTTPS


    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:QtQt5.9.1Toolsmingw530_32optin。

    把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

    ---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

    ---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
    ", err);
    }
    

      

    4.3 UI

    <?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

  • 相关阅读:
    JavaScript——math对象
    JavaScript——日期相关
    JavaScript——数组与数组方法
    JavaScript——数值
    JavaScript——字符串方法
    关于CSS的一些问题(一)
    html5新增标签
    svg
    在自定义组件中使用v-model
    百度地图定位
  • 原文地址:https://www.cnblogs.com/rockyching2009/p/15474343.html
Copyright © 2020-2023  润新知