QtWebApp开发https服务器,完成客户端与服务器基于ssl的双向认证,纯代码操作

news2024/12/23 5:01:33

引言:所谓http协议,本质上也是基于TCP/IP上服务器与客户端请求和应答的标准,web开发中常用的http server有apache和nginx。Qt程序作为http client可以使用QNetworkAccessManager很方便的进行http相关的操作。Qt本身并没有http server相关的库,也许是因为很少有这种需求吧。但是实际开发中也会有做简单的http server的需求。实际上QT6.4里面引入了基于http的服务器,但是看了下,也是非常的鸡肋。

大概有以下几个基于QT/C++的http服务器的库。

一、qthttpserver

前面说到,Qt官方在发行版中并没有提供相应的类来实现HTTP 服务端,但是Qt-lab中提供了一个qthttpserver,​ ​Github地址在这里​​,不知道基于什么原因Qt官方没有将其纳入正式的Qt发行版中,如果要使用qthttpserver的话,就得自己下载源码进行编译了,不过编译可没那么容易,会遇到各种错误,而且还没有提供说明文档,这就不太友好了。
关于编译或简单使用参考以下文章:

Qt开发的轻量级http服务器-QtHttpServer编译
关于Qt HttpServer的一些测试(Qt6.4.0rc)
QtHttpServer编译使用,亲测可用
QT基于qhttp-server搭建http服务器

二、cpp-httplib

这是Github上一个开源的C++实现http server的项目,使用方法非常简单,就只有一个头文件,引入到自己的项目中就可以使用了,不过这是一个多线程“阻塞”HTTP 库,如果您正在寻找“非阻塞”库,那就要换别的了。具体使用可以参考Github里面的文档介绍,很详细。​ ​

三、 QtWebApp

QtWepApp 是 C++ 中的 HTTP 服务器库,适用于 Linux、Windows、Mac OS 和Qt 框架支持的许多其他操作系统。QtWebApp要求Qt版本至少是 Qt5.15 及其以上
这个库使用也非常简单,并且很明显它是用QT写的,所以与Qt的项目可以无缝衔接。
但并不是QT官方写的,不过它的名气比Qt官方qthhttpserver库要大。

有趣的是,Qt制造商多年来一直在开发标准HTTP服务器,但到2022年,它仍然不包括在Qt库中。这也许可以解释为什么很多人使用的库。
实际上QT6.4里面引入了基于http的服务器,但是看了下,也是非常的鸡肋。

本次选择更为实用的QtWebApp来作为http服务器。

源码下载:http://www.stefanfrings.de/qtwebapp/index-en.html
Project homepage:http://stefanfrings.de/qtwebapp/index-en.html
Tutorial:http://stefanfrings.de/qtwebapp/tutorial/index.html
API doc:http://stefanfrings.de/qtwebapp/api/index.html

在这里插入图片描述

关于QtWebApp的使用,可以直接阅读源码提供的demo1,结合日志可以很快的理解。
更好的是直接参考QtWebApp的使用网站:Tutorial,真的是非常详细,,不过是英文版的,需要具备一定的英语阅读能力。网站如下图所示。

在这里插入图片描述
可以直接把源码模块直接引入到工程里面使用,源码里面就3个模块{httpserver、logging、templateengine},按需引入即可,,,httpserver模块是最核心的,肯定是要引入的。。。
也可以将源码编译成库,,然后再通过头文件+库的形式去倒腾,,,,
推荐直接引入源码,首先QtWebApp的模块并不大,,而且可以随时调试源码或修改源码,如下文陈述,就涉及到了修改源码。

四、双向认证

之前,使用nginx作为https服务器,与客户端进行了双向认证和单向认证。
参考以下两篇文章,本篇是在以下两篇文章的基础上继续开发的,,证书、私钥等的制作及流程验证在以下文章中均作了详细的说明。所以,阅读此篇文章前务必首先阅读以下两篇文章。

ssl单向证书和双向证书校验测试及搭建流程
QT充当客户端模拟浏览器等第三方客户端对https进行双向验证

此处,采用QtWebApp来手动开发https服务器,来完成与客户端之间基于ssl的双向认证
客户端:win10 + Qt5.9.9,192.168.64.1
服务端:win10 + Qt6.4, 192.168.64.176

此时直接将QtWebApp的demo1拷过来,并在配置文件中指定证书及秘钥,证书和秘钥在之前的文章中都倒腾过,并且直接使用之前做好的,可以参考ssl单向证书和双向证书校验测试及搭建流程一文。
并将配置文件的端口改为443,这是https的默认端口,,(当然也可以设置为其他端口,写为443时在客户端向服务器发起请求时无需手动指定端口号,否则必须手动指定端口号)

在这里插入图片描述

此时运行https服务,发现报错如下,,,,,,,说白了,要想支持https,Qt的版本至少是5.15。。
在这里插入图片描述

好,此时去虚拟机中安装Qt6.4。。
安装完毕后,,运行程序,此时报错缺少 core5compat模块。。。

在这里插入图片描述

于是,去Qt里添加 Qt 5 Compatibility Module组件,,
在这里插入图片描述

倒腾完成后,此时运行,一切OK。。接下来首先确保主机和虚拟机互ping通(注意关闭防火墙)。
结果访问 https://192.168.64.176 时,无任何响应。。
wireshark抓包观察,结果可以明显看出ssl并没有建立成功,client发出第一次握手clienthello时,服务端压根没有任何回应。。。。服务端连最基本的serverhello都没有回应。。

在这里插入图片描述
好,再通过curl工具(可以看作是客户端)看看,,可以看出curl访问时是一直在那儿等着服务端的回应。。
但服务端那边迟迟无响应,,最终握手失败。

curl -k --cert ./client.crt --key ./client.key --show-error  https://192.168.64.176 -v

请添加图片描述
在这里插入图片描述

ssl握手失败的情况比较多,,可以参考下两篇文章:https://zhuanlan.zhihu.com/p/572516140
https://www.jiyik.com/tm/xwzj/network_287.html ,说实话,此时你压根就不知道是哪个环节出了问题。。这两篇文章只能是大概看看,有点印象罢了。。。

好,我们来看看服务端的日志,,,

在这里插入图片描述

可以发现,实际上没有任何有用的信息。。
此时只能去阅读QtWebApp的源码。。= > 柳暗花明又一村。。。

在这里插入图片描述

因为https服务器的创建肯定是要加载服务端证书和私钥的,,那就只好去看看它是怎么加载的,,在httpconnectionhandlerpool.cpp 源码的140行处是加载服务端私钥的地方。因为之前在通过openssl创建服务端的私钥时指定了私钥的密码123456,而源码中通过QSSLKey创建秘钥时却没有指定密码,,,所以导致出现了客户端发起握手请求clienthello时,服务端无动于衷,,,实际上就是因为服务器加载私钥有误导致的。。。这里吐槽下,QtWebApp这地方处理的不好,,这种错误应该是要以日志的形式呈现给调试者的,,,,,最终导致握手失败。。。

修改完源码后,此时重新启动服务端程序,,,先用curl先测试一把,,
在这里插入图片描述
果然,此时握手已经成功了,客户端也接受了来自服务端的响应。
从此处我们也可以看出,,之前我们一开始通过curl工具握手失败时,分析查看了curl的相关提示,,,以及服务端QtWebApp的日志,都没有看出有价值的提示,,,所以这种情况下,还是需要去通过分析以及修改源码大法来倒腾的,,,

【注意】此处使用curl命令之前需要首先导入客户端p12格式的证书,可阅读此篇文章:ssl单向证书和双向证书校验测试及搭建流程 中的双向证书校验小节内容,此处不再赘述。。。
【值得注意的是】:在 [ssl单向证书和双向证书校验测试及搭建流程] 一文中使用curl工具验证时,并没有事先导入客户端p12格式的证书就直接验证了,也是可以的,当然了,那时的https服务器是基于linux下的nginx的此处验证时发现必须首先导入客户端p12格式的证书,否则直接响应连接失败:schannel: failed to receive handshake, SSL/TLS connection failed具体原因未知,,待后倒腾。。
在这里插入图片描述

好,抓包看看,客户端和服务器之间是否向彼此发送了证书。
在这里插入图片描述
答案是肯定的,,因为只有双向认证的情况下,客户端才会向服务端发送证书。

书归正传,此时,我们已经通过QtWebApp以代码的形式创建了https服务器,,
接下来,我们的客户端就要通过我们自己编写的代码去替代刚才的curl工具或浏览器这种集成的第三方客户端去向服务器发起https请求,,,

void MainWindow::testSsl()
{
    // 加载客户端证书
    QFile crtFile("C:\\Users\\XingWei\\Documents\\untitled19\\client.crt");
    crtFile.open(QIODevice::ReadOnly);
    QSslCertificate certificate(&crtFile, QSsl::Pem);
    crtFile.close();

    // 加载客户端私钥
    QByteArray passPhrase("123456");  // 私钥的密码
    QFile keyFile("C:\\Users\\XingWei\\Documents\\untitled19\\client.key");
    keyFile.open(QIODevice::ReadOnly);
    QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, passPhrase);
    keyFile.close();

    // ssl配置
    QSslConfiguration conf;
    conf.setPeerVerifyMode(QSslSocket::QueryPeer);
    conf.setProtocol(QSsl::TlsV1SslV3);
    conf.setPrivateKey(privateKey);
    conf.setLocalCertificate(certificate);

    QNetworkRequest request;
    request.setUrl(QUrl("https://192.168.64.176"));
    request.setSslConfiguration(conf);

    QEventLoop loop;
    QNetworkReply* reply = m_manager.post(request, QByteArray("AAAAAAAAAAAAAAAAAAAAA"));
    connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    connect(reply, &QNetworkReply::finished, this, [=] {

        qDebug() << "####################### get data finished...";

        QByteArray data = reply->readAll();
        qDebug() << data;
    });
    loop.exec();

    qDebug() << "#############> " << reply->errorString() << reply->error();
}

运行效果及抓包如下所示:
请添加图片描述

客户端和服务端建立起ssl通道后就可以将数据加密交互了。。

好,接下来,我们在https服务器中拦截来自客户端的不同请求路径,进而做出相应的回应。
好,在客户端通过向服务器发送不同的请求路径,看看响应效果如何。。。
因为demo1中提供了几个请求路径,此处就以demo1提供的请求路径为基点来测试。。。
并在服务端新增一个/hello的请求路径,,该路径向客户端响应给以HELLO,SSL.字符串。

修改上述的客户端代码如下:

// ...
QNetworkRequest request;
request.setUrl(QUrl("https://192.168.64.176/template"));
//    request.setUrl(QUrl("https://192.168.64.176/dump"));
//    request.setUrl(QUrl("https://192.168.64.176/hello"));
request.setSslConfiguration(conf);

QEventLoop loop;
QNetworkReply* reply = m_manager.get(request);
// ...

服务端新增HelloController请求处理类相关代码:

在这里插入图片描述

void HelloController::service(HttpRequest &request, HttpResponse &response)
{
   response.setHeader("Content-Type", "text/html; charset=UTF-8");

   response.write("<html><body>");
   response.write("<h1>");
   response.write("HELLO, SSL.");
   response.write("</h1>");
   response.write("</body></html>",true);
}

在这里插入图片描述

访问/template路径时https服务器的响应:

"<html><body>\r\n\r\nHello,<br>\r\nyou requested the path: /template\r\n<p>\r\nAnd your web browser provided the following headers:\r\n<p>\r\n\r\n  <b>accept-encoding:</b> gzip, deflate<br>\r\n\r\n  <b>accept-language:</b> zh-CN,en,*<br>\r\n\r\n  <b>connection:</b> Keep-Alive<br>\r\n\r\n  <b>host:</b> 192.168.64.176<br>\r\n\r\n  <b>user-agent:</b> Mozilla/5.0<br>\r\n\r\n\r\n</html></body>\r\n"

访问/dump时的响应:

"<html><body><b>Request:</b><br>Method: GET<br>Path: /dump<br>Version: HTTP/1.1<p><b>Headers:</b><br>accept-encoding=gzip, deflate<br>accept-language=zh-CN,en,*<br>connection=Keep-Alive<br>host=192.168.64.176<br>user-agent=Mozilla/5.0<p><b>Parameters:</b><p><b>Cookies:</b><p><b>Body:</b><br></body></html>"

访问/hello时的响应:

"<html><body><h1>HELLO, SSL.</h1></body></html>"

用浏览器来验证一下我们的请求,更加直观,如下所示:
【注意】:此处使用浏览器验证时,必须首先导入客户端p12格式的证书,否则是无法访问的。
关于客户端证书的导入,阅读此篇文章:ssl单向证书和双向证书校验测试及搭建流程中的双向证书校验小节内容,此处不再赘述。。。
请添加图片描述

由此可见,一切正常。
当https建立后,之前基于http写的请求路径以及对应的处理函数都不用作任何改变,直接向访问http那样访问就可以了,只不过此时需要变成https了。。
由此可见,https只不过是在http的请求基础上加了一层加密。

关于http的明文传输和https的加密传输,可以通过抓包来看。
可进一步阅读 QtWebApp同时开启http服务和https服务,接受来自客户端的不同请求并进行相应的处理 一文中 测试效果小节 所对应的内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/841281.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年实验班招生考试题解

比赛连接&#xff1a;传送门 密码&#xff1a;2023qsb A.Zlzs problem(Easy Version) 题目描述&#xff1a; This is the easy version of this problem. The only difference between the easy and hard versions is the constraints on n and m. So I wont even take a g…

1、Spark SQL 概述

1、Spark SQL 概述 Spark SQL概念 Spark SQL is Apache Spark’s module for working with structured data. 它是spark中用于处理结构化数据的一个模块 Spark SQL历史 Hive是目前大数据领域&#xff0c;事实上的数据仓库标准。 Shark&#xff1a;shark底层使用spark的基于…

项目中使用git vscode GitHubDesktopSetup-x64

一、使用git bash 1.使用git bash拉取gitee项目 1.在本地新建一个文件夹&#xff08;这个文件夹是用来存放从gitee上拉下来的项目的&#xff09; 2.在这个文件夹右键选择 git bash here 3.输入命令 git init (创建/初始化一个新的仓库) 4.输入命令 git remote add origin …

opencv基础41-图像梯度-sobel算子详解cv2.Sobel()(边缘检测基础)

图像梯度是用于描述图像变化率的概念。在图像处理中&#xff0c;梯度指的是图像中每个像素的灰度值变化速率或方向。它常用于边缘检测和图像特征提取。 一维图像的梯度表示为函数 f(x) 的导数&#xff0c;而在二维图像中&#xff0c;梯度是一个向量&#xff0c;包含两个分量&am…

《HeadFirst设计模式(第二版)》第六章代码——命令模式

代码文件目录&#xff1a; Command package Chapter6_CommandPattern.Command;/*** Author 竹心* Date 2023/8/6**/public interface Command {public void execute();public void undo();//撤销该指令 }CeilingFan package Chapter6_CommandPattern.ElectricAppliance;/*** …

阿里云快速部署开发环境 (Apache + Mysql8.0+Redis7.0.x)

本文章的内容截取于云服务器管理控制台提供的安装步骤&#xff0c;再整合前人思路而成&#xff0c;文章末端会提供原文连接 ApacheMysql 8.0部署MySQL数据库&#xff08;Linux&#xff09;步骤一&#xff1a;安装MySQL步骤二&#xff1a;配置MySQL步骤三&#xff1a;远程访问My…

协议,序列化,反序列化,Json

文章目录 协议序列化和反序列化网络计算器protocol.hppServer.hppServer.ccClient.hppClient.cclog.txt通过结果再次理解通信过程 Json效果 协议 协议究竟是什么呢&#xff1f;首先得知道主机之间的网络通信交互的是什么数据&#xff0c;像平时使用聊天APP聊天可以清楚&#x…

【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

网络编程套接字 &#x1f41b;预备知识&#x1f98b;理解源IP地址和目的IP地址&#x1f40c;认识端口号&#x1f41e; 理解 "端口号" 和 "进程ID"&#x1f41c;简单认识TCP协议&#x1f99f;简单认识UDP协议&#x1f997; 什么是网络字节序 &#x1f577;相…

MySQL的关键指标及采集方法

MySQL 是个服务&#xff0c;所以我们可以借用 Google 四个黄金指标的思路来解决问题。 1、延迟 应用程序会向 MySQL 发起 SELECT、UPDATE 等操作&#xff0c;处理这些请求花费了多久&#xff0c;是非常关键的&#xff0c;甚至我们还想知道具体是哪个 SQL 最慢&#xff0c;这样…

【Linux】进程间通信——System V信号量

目录 写在前面的话 一些概念的理解 信号量的引入 信号量的概念及使用 写在前面的话 System V信号量是一种较低级的IPC机制&#xff0c;使用的时候需要手动进行操作和同步。在现代操作系统中&#xff0c;更常用的是POSIX信号量&#xff08;通过sem_*系列的函数进行操作&…

linux系统虚拟主机开启支持Swoole Loader扩展

特别说明&#xff1a;只是安装支持Swoole扩展&#xff0c;主机并没有安装服务端。目前支持版本php5.4-php7.2。 1、登陆主机控制面板&#xff0c;找到【远程文件下载】这个功能。 2、远程下载文件填写http://download.myhostadmin.net/vps/SwooleLoader_linux.zip 下载保存的路…

SpringBoot 升级内嵌Tomcat

SpringBoot 更新 Tomcat 最近公司的一个老项目需要升级下Tomcat&#xff0c;由于这个项目我完全没有参与&#xff0c;所以一开始我以为是一个老的Tomcat项目&#xff0c;升级它的Tomcat依赖或者是Tomcat容器镜像&#xff0c;后面发现是一个SpringBoot项目&#xff0c;升级的是…

加速中产 “返贫” 的4个迹象

没有消息&#xff0c;就是好消息。这话放在现在的朋友圈子里&#xff0c;似乎很合适。最近接到两个朋友的电话&#xff0c;一个是朋友的诉苦电话&#xff0c;这位朋友曾是某大厂的高管&#xff0c;被裁后失业近1年&#xff0c;虽然当初赔了N1&#xff0c;但架不住这位朋友“房贷…

Maven 打包生成Windows和Liunx启动文件

新建一个springboot项目。 1、项目结构 2、pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocati…

ASP.NET Core MVC -- 将视图添加到 ASP.NET Core MVC 应用

Index页 右键单击“视图”文件夹&#xff0c;然后单击“添加”>>“新文件夹”&#xff0c;并将文件夹命名为“HelloWorld”。 右键单击“Views/HelloWorld”文件夹&#xff0c;然后单击“添加”>“新项”。 在“添加新项 - MvcMovie”对话框中&#xff1a; 在右上…

Misc取证学习

文章目录 Misc取证学习磁盘取证工具veracryto挂载fat文件DiskGenius 磁盘取证例题[RCTF2019]disk 磁盘[](https://ciphersaw.me/ctf-wiki/misc/disk-memory/introduction/#_2)内存取证工具volatility 内存取证例题数字取证赛题0x01.从内存中获取到用户admin的密码并且破解密码 …

gin和gorm框架安装

理论上只要这两句命令 go get -u gorm.io/gorm go get -u github.com/gin-gonic/gin然而却出现了问题 貌似是代理问题&#xff0c;加上一条命令 go env -w GOPROXYhttps://goproxy.cn,direct 可以成功安装 安装gorm的数据库驱动程序 go get -u gorm.io/driver/mysql

香港人力资源服务商迦里仕人才,申请纳斯达克IPO上市

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于香港的人力资源服务商迦里仕人才&#xff08;Galaxy Payroll Group&#xff09;近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&am…

VUE框架:vue2转vue3全面细节总结(4)滚动行为

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人_python人工智能视觉&#xff08;opencv&#xff09;从入门到实战,前端,微信小程序-CSDN博客 最新的uniapp毕业设计专栏也放在下方了&#xff1a; https://blog.csdn.net/lbcy…

Redis可视化工具

Redis可视化工具 1、RedisInsight 下载地址&#xff1a;https://redis.com/redis-enterprise/redis-insight/ 双击软件进行安装&#xff0c;安装完后弹出如下界面&#xff1a; 安装完成后在主界面选择添加Redis数据库&#xff1b; 选择手动添加数据库&#xff0c;输入Redis…