HTTP 摘要认证

文章目录

  • 一、什么是摘要认证
  • 二、工作流程
  • 三、实例演示


一、什么是摘要认证

摘要认证,即 Digest Access Authentication,是一种HTTP身份验证机制,用于验证用户的身份。相较于基本认证(Basic Authentication)使用用户名密码的方式,提供了更高的安全性和灵活性。

二、工作流程

HTTP Digest Access Authentication 的工作流程如下:

  1. 客户端发送一个未经认证的请求到服务器
  2. 服务器返回一个 HTTP 401 Unauthorized 响应,其中包含一个 “WWW-Authenticate” 头部字段,用来表示所使用的认证方式(通常是 Digest),以及一些额外的参数,如 realm(领域)、nonce(随机数)等
  3. 客户端收到 401 响应后,会根据服务器提供的信息,计算出一个摘要(digest)。客户端将摘要信息添加到请求中的 “Authorization” 头部字段中,重新发送请求到服务器
  4. 服务器收到带有摘要的请求后,会使用相同的算法计算出一个期望的摘要,并与客户端提供的摘要进行比较。如果两者一致,则服务器会接受该请求,返回请求的资源;否则,服务器拒绝请求,可能返回 401 或其他适当的响应

三、实例演示

客户端调用服务器 API 发送请求:

class CHttpRequest : public QObject {
    Q_OBJECT
    
   public:
    int sendFile(const QString &strUrl, QString &strFile, QString &strAuth, QString &recvMessage) {
        m_bNeedAuth = false;
        disconnect(m_pNetworkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(slot_requestFinished(QNetworkReply *)));  // 请求完成信号
        connect(m_pNetworkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(slot_requestRet_Syn(QNetworkReply *)));
        // 构造请求头
        QNetworkRequest netRequest(strUrl);
        QString boundary = "----WebKitFormBoundaryyYCL2Hd3ZwCR4KhI";
        QString contenType = "multipart/form-data; boundary=" + boundary;
        netRequest.setHeader(QNetworkRequest::ContentTypeHeader, contenType);
        if (strAuth != "") {
            netRequest.setRawHeader("Authorization", strAuth.toLatin1());
        }
        // 打开文件
        QFile file(strFile);
        if (!file.open(QIODevice::ReadOnly)) {
            return -1;
        }
        // 构建请求数据
        strFile.remove(0, strFile.lastIndexOf('/') + 1);
        QString text = "Content-Disposition: form-data; name=\"fimage\"; filename=\"" + strFile + "\"\r\n";
        QByteArray data;
        data.append("--" + boundary + "\r\n");
        data.append(text);
        data.append("Content-Type: application/octet-stream\r\n\r\n");
        data.append(file.readAll());
        data.append("\r\n--" + boundary + "--\r\n");
        // 发送POST请求
        m_pNetworkReply = m_pNetworkManager->post(netRequest, data);
       
        // ...省略部分代码
   
        recvMessage = this->m_strRet;
        if (m_bNeedAuth) {
            return 401;
        }
        return m_loop_flag;
    }

   public slots:
    // 接收服务器返回信息
    void slot_requestRet_Syn(QNetworkReply *reply) {
        // ...省略部分代码
        QByteArray resultContent = reply->readAll();
        QTextCodec *pCodec = QTextCodec::codecForName("UTF-8");
        QString strResult = pCodec->toUnicode(resultContent);
        int nHttpCode = reply>attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); // http返回码
        if (nHttpCode == 200) {  // 请求成功

        } else if (nHttpCode == 401) {  // 请求失败,想要认证
            // 获取服务器返回的WWW-Authenticate认证信息
            QList<QByteArray> headers = reply->rawHeaderList();
            for (QList<QByteArray>::const_iterator iter = headers.constBegin(); iter != headers.constEnd(); ++iter) {
                qDebug() << *iter << ":" << reply->rawHeader(*iter);
                QString msg = *iter;
                if (msg.contains("WWW-Authenticate")) {
                    strResult = reply->rawHeader(*iter);
                }
            }
        } else {  // 其他错误

        }
    }
    
   public:
    // 计算验证信息的函数,这里的计算规则是我这台服务的规定,大家处理的时候要根据自己服务器的规则去处理
    QString reAuthenticate(QString &strUrl, QString &strUser, QString &strPsw, QString &httpHeadFromServer) {
        QString realm, qop, nonce, opaque;
        int lastIndex = httpHeadFromServer.lastIndexOf("Digest realm=");
        httpHeadFromServer = httpHeadFromServer.mid(lastIndex);

        realm = httpHeadFromServer.section("realm=\"", 1, 1).split('"').first();
        qop = httpHeadFromServer.section("qop=\"", 1, 1).split('"').first();
        nonce = httpHeadFromServer.section("nonce=\"", 1, 1).split('"').first();
        opaque = httpHeadFromServer.section("opaque=\"", 1, 1).split('"').first();

        QString A1 = strUser + ":" + realm + ":" + strPsw;
        QByteArray hashedA1 = QCryptographicHash::hash(A1.toUtf8(), QCryptographicHash::Sha256);
        QString strHashA1 = hashedA1.toHex();

        QString A2 = "POST:" + strUrl;
        QByteArray hashedA2 = QCryptographicHash::hash(A2.toUtf8(), QCryptographicHash::Sha256);
        QString strHashA2 = hashedA2.toHex();

        QString response = strHashA1 + ":" + nonce + ":00000001:b985236c7eb52970:auth:" + strHashA2;
        QByteArray hashedRes = QCryptographicHash::hash(response.toUtf8(), QCryptographicHash::Sha256);
        QString strRes = hashedRes.toHex();

        QString strAuth = " Digest username=\"" + strUser + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" +
            strUrl + "\", algorithm=SHA-256, response=\"" + strRes + "\", opaque=\"" + opaque + "\", qop=" + qop +
            ", nc=00000001, cnonce=\"b985236c7eb52970\"";
        return strAuth;  // 处理好的认证信息
    }
};

int main() {
    QString strAuth = "";
    CHttpRequest checkRequest;
    QString strURL = "http://172.16.26.165/setup/system/update.php?app=set";
    int ret = checkRequest.sendFile(strURL, strTmpFile, strAuth, strRecv);  // 第一次调用API时,认证信息strAuth是空的
    if (ret == 401) {  // 服务器返回401
            QString url = "/setup/system/update.php?app=set";
            QString user = "admin";
            QString psw = "12345";
            strAuth = checkRequest.reAuthenticate(url, user, psw, strRecv);  // 根据服务器定的规则,计算验证信息
            // 使用计算出来的验证信息,重新请求API
            CHttpRequest checkRequest2;
            if (checkRequest2.sendFile(strURL, strTmpFile, strAuth, strRecv) != -1) {
                emit msgUpgradeSta(this, Upgrade_Success);
            } else {
                emit msgUpgradeSta(this, Upgrade_Fail);
            }
            return 0;
        }
    return 0;
}

如上代码,客户端想通过 HTTP POST 请求,发送一个文件给服务器。第一次调用 API 的时候,没有带上验证信息 Authorization,服务器返回 401 错误,并且在返回的 HTTP 头部信息中带有 WWW-Authenticate 认证信息:

HTTP/1.1 401 Unauthorized
Set-Cookie: PHPSESSID=984d2f37f1611d4848518ca643ccbfa0; path=/; HttpOnly
Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
WWW-Authenticate: Digest realm="N5M-6S335",algorithm="MD5",qop="auth",nonce="6444da9264a78",opaque="51d97021ccdf09ef7a8da27e8193cabf"
WWW-Authenticate: Digest realm="N5M-6S335",algorithm="SHA-256",qop="auth",nonce="6444da9264a78",opaque="51d97021ccdf09ef7a8da27e8193cabf"
Content-type: text/html; charset=UTF-8
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Content-Length: 0
Date: Sun, 23 Apr 2023 07:13:21 GMT
Server: WintenDolighttpd/1.4.67

在 slot_requestRet_Syn 函数中截取 WWW-Authenticate 的内容(可以看到有两种加密方式,MD5 和 SHA-256)。获取到需要的验证信息后,main 函数调用 reAuthenticate 函数,根据服务器定的规则进行验证信息的处理,这里选择 SHA-256 的加密方式,所以代码里截取出WWW-Authenticate: Digest realm="N5M-6S335",algorithm="SHA-256",qop="auth",nonce="6444da9264a78",opaque="51d97021ccdf09ef7a8da27e8193cabf这一行信息,再分别截取出 realm、qop 等等这些字段,根据规则进行 SHA-256 加密,最终得出认证信息。最后,main 函数重新发送 HTTP 请求,在请求头的 Authorization 字段加上这串验证信息:

示例图片

这样,就可以成功请求服务器了。文中用到的 HTTP 请求类,可以到点击这里下载。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt快速入门到熟练(3.程序运行发布与设置图标)

程序运行发布 当我们执行过qt过后&#xff0c;将会在项目目录里面生成出一个debug构建目录&#xff0c;点击进去选择debug文件夹&#xff0c;就可以看到我们生成出来的可执行文件。 很显然我们的项目就叫做MyFirstWidget&#xff0c;所以生成的可执行文件在没有人为设置的情…

CLR学习

视频链接&#xff1a;《CLR十分钟》系列之CLR运行模型_哔哩哔哩_bilibili 什么是 CLR 公共语言运行时&#xff08;Common Language Runtime CLR&#xff09; 是一个可有多种编程语言使用的 运行时&#xff0c;CLR 的核心功能&#xff08;比如 内存管理&#xff0c;程序集加载…

notion的使用心得

从老石的视频知道了notion是一个很强大的管理工具&#xff1a;这就是最棒的效率软件&#xff01;如果不是&#xff0c;我倒想试试你的 | Notion使用技巧分享_哔哩哔哩_bilibili 我一时半会不能全部学会&#xff0c;但是借用大家的好模板&#xff1a;如何用5分钟搭建简洁高效的…

html5分步问卷调查表模板源码

文章目录 1.设计来源1.1 问卷调查11.2 问卷调查21.3 问卷调查31.4 问卷调查41.5 问卷调查51.6 问卷调查6 2.效果和源码2.1 完整效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/137454703 html5分…

React 项目配置代码提交规范 ESLint、Pretttier、Husky、CommitLint

React 项目配置代码提交规范 ESLint、Pretttier、Husky、CommitLint 前言 团队开发的成员越来越多&#xff0c;项目都是由多个人进行开发和维护&#xff0c;每个人的代码书写习惯和风格又不尽相同&#xff0c;commit 的提交log 也是乱七八糟&#xff0c;为以后的开发和维护增…

PPT在线压缩工具推荐

有时候使用邮箱发送邮件时&#xff0c;添加的PPT、Word、PDF文档总会因为过大而转为其他类型的附件发送&#xff0c;不仅上传缓慢&#xff0c;对方查收下载时还有有效期限制&#xff0c;7天或15天后就过期再也无法下载了&#xff0c;有没有什么办法可以压缩PPT等文档&#xff0…

python3内置持久化模块shelve心得

python3内置持久化模块shelve心得 来自python官方网站的解释&#xff1a; https://docs.python.org/zh-cn/3.10/library/shelve.html 本文环境&#xff1a; Windows 10 专业版 64 位 Thonny 3.2.6 概述 内置模块 shelve 可以将任意 Python 对象&#xff08;即 https://docs…

第29篇:秒表计时器

Q&#xff1a;本期我们采用计数器来实现秒表计时器&#xff0c;循环进行0~9计时。 A&#xff1a;在数码管HEX0上循环从0到9计数&#xff0c;间隔时间为1s&#xff0c;使用计数器实现1s时间间隔。 DE2-115开发板提供了50MHz时钟&#xff0c;触发器直接以50MHz信号作为同步时钟…

C++ | Leetcode C++题解之第11题盛最多水的容器

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxArea(vector<int>& height) {int l 0, r height.size() - 1;int ans 0;while (l < r) {int area min(height[l], height[r]) * (r - l);ans max(ans, area);if (height[l] < height[r…

鸿蒙实现一种仿小红书首页滑动联动效果

前言&#xff1a; DevEco Studio版本&#xff1a;4.0.0.600 效果描述&#xff1a;通过手指滑动列表&#xff0c;控制位置显示效果为&#xff1a;不论列表在什么位置下滑时下图粉色位置布局显示&#xff0c;手指上滑时下图粉色位置布局隐藏。 效果&#xff1a; 原理分析&…

简单介绍css及其代码样式

css简介 css用于前端开发&#xff0c;负责对界面进行美化。让页面更美观。 他可以改变html代码的样式&#xff0c;让html代码的网页不那么死板。 css代码格式 选择器 {属性:值; 属性:值&#xff1b;} css的模版架构 css代码放到<style>标签中。 而<style>通常是…

2024/4/7周报

文章目录 摘要Abstract文献阅读题目引言创新点Decoder-Encoder模型实验过程实验结果 深度学习LSTM变体Bidirectional LSTM&#xff08;双向LSTM&#xff09;GRUGRU代码实现 总结 摘要 用于统计机器翻译的RNN编码器-解码器学习短语表示 文中提出了一种新的神经网络模型称为RNN编…

博客评论回复03

接着之前写的&#xff0c;之前返回的数据集按道理来说渲染出来还是丑丑的&#xff0c;因此这次我看着抖音的评论样子&#xff0c;自己瞎写了一通&#xff0c;不过也算是模仿出来了虽然肯定没有抖音写的好。 类似与前面几章写的表结构 首先看看抖音评论区是怎么样的&#xff1f…

消息队列之RabbitMQ的安装配置

一&#xff0c;前言 RabbitMQ是由erlang语言开发&#xff0c;基于AMQP&#xff08;Advanced Message Queue 高级消息队列协议&#xff09;协议实现的消息队列&#xff0c;它是一种应用程序之间的通信方法&#xff0c;消息队列在分布式系统开发中应用非常广泛。点击跳转RabbitM…

前端开发学习笔记 3 (Chrome浏览器调试工具、Emmet语法、CSS复合选择器、CSS元素选择模式、CSS背景)

文章目录 Chrome浏览器调试工具Emmet语法CSS复合选择器后代选择器子选择器并集选择器伪类选择器 CSS元素选择模式元素选择模式概述CSS块标签CSS行内标签CSS行内块标签CSS元素显示模式转换 CSS背景CSS背景颜色CSS背景图片CSS背景图片平铺CSS背景图片位置CSS背景图片固定CSS背景复…

HIS系统是什么?一套前后端分离云HIS系统源码 接口技术RESTful API + WebSocket + WebService

HIS系统是什么&#xff1f;一套前后端分离云HIS系统源码 接口技术RESTful API WebSocket WebService 医院管理信息系统(全称为Hospital Information System)即HIS系统。 常规模版包括门诊管理、住院管理、药房管理、药库管理、院长查询、电子处方、物资管理、媒体管理等&…

275. 传纸条(DP)

题目描述 小渊和小轩是好朋友也是同班同学&#xff0c;他们在一起总有谈不完的话题。一次素质拓展活动中&#xff0c;班上同学安排坐成一个 m 行 n 列的矩阵&#xff0c;而小渊和小轩被安排在矩阵对角线的两端&#xff0c;因此&#xff0c;他们就无法直接交谈了。幸运的是&…

CMU15/445 2023 Spring-project1 LRU-K 替换策略

在写个demo之前&#xff0c;专门学习了LRU:【LeetCode刷题】146. LRU 缓存-CSDN博客 使用哈希表 双向链表可以满足删除/增加的时间复杂度为O(1)。 在通读完15/445这块的说明之后&#xff0c;发现和LRU还是有些差别的。 官方文档中对LRU-K的解释是&#xff1a;LRU-K算法根据所…

Spring boot框架Rouyi Cloud入门之token

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

【Gluten】Spark 的向量化执行引擎框架 Gluten

Gluten 项目主要用于“粘合” Apache Spark 和作为 Backend 的 Native Vectorized Engine。Backend 的选项有很多&#xff0c;目前在 Gluten 项目中已经明确开始支持的有 Velox、Clickhouse 和 Apache Arrow。通过使用Native backend 执行计算&#xff0c;加速 Spark 执行速度&…