【ONE·Linux || 网络基础(三)】

总言

  主要内容:HTTP和HTTPS工作方式简述。

文章目录

  • 总言
  • 6、HTTP协议(应用层·二)
    • 6.1、入门认识
      • 6.1.1、认识URL
      • 6.1.2、urlencode和urldecode
    • 6.2、快速构建
      • 6.2.1、快速构建http请求和响应的报文格式
      • 6.2.2、http demo
        • 6.2.2.1、sock.hpp && httpserver.hpp
        • 6.2.2.2、httpserver.cc:试着读取一个http请求并构建一个http的响应正文
    • 6.3、逐步细化
      • 6.3.1、web根目录
        • 6.3.1.1、基本说明
        • 6.3.1.2、访问一个静态网页:util.hpp、index.html
      • 6.3.2、http的请求方法
        • 6.3.2.1、基本说明
        • 6.3.2.2、在命令行中使用telnet命令测试GET方法
        • 6.3.2.3、使用表单来测试GET、POST方法
      • 6.3.3、http状态码
      • 6.3.4、HTTP常见Header(报头)
      • 6.3.5、会话管理(Cookie、Session)
      • 6.3.6、短连接与长连接
  • 7、HTTPS协议(应用层·三)
    • 7.1、https是什么
    • 7.2、预备知识
      • 7.2.1、加密:是什么和为什么
      • 7.2.2、常见加密方式(对称加密 && 非对称加密)
      • 7.2.3、数据摘要(数据指纹) && 数字签名
    • 7.3、HTTPS 的工作过程探究
      • 7.3.1、方案 1:只使用对称加密
      • 7.3.2、方案 2:只使用非对称加密
      • 7.3.3、方案 3:双方都使用非对称加密
      • 7.3.4、方案 4:非对称加密 + 对称加密
      • 7.3.5、中间人攻击与CA证书认证流程
      • 7.3.6、方案 5 : 非对称加密 + 对称加密 + 证书认证

  
  
  

6、HTTP协议(应用层·二)

6.1、入门认识

  应用层就是程序员基于socket接口之上编写的具体逻辑,其中大部分工作和文本处理有关,也就是协议分析与处理。http协议作为现成的应用层协议,其中一定会具有大量的文本分析和协议处理!
  

6.1.1、认识URL

  平时我们俗称的 “网址” 其实就是说的 URL。
在这里插入图片描述

  因url能够定位互联网中唯一的一个资源,故url也称为统一资源定位符 ( Uniform Resource Locator,统一资源定位器 )。基于此方式获取资源,称其为www万维网(World Wide Web的简称,也称为Web、3W等。www是基于客户机/服务器方式的信息发现技术和超文本技术的综合。)
  
  
  

6.1.2、urlencode和urldecode

  说明: 如果用户想在url中,包含url本身用来作为特殊字符的字符(如 /? 等),url形式的时候, 浏览器会自动给我们进行编码(encode),即对特殊字符进行转义。一般服务端收到之后, 需要进行转回特殊字符。
  PS:urldecode就是urlencode的逆过程。

在这里插入图片描述

  
  转义的规则如下: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。
  PS:汉字也是如此。
在这里插入图片描述

  
  
  如果有需求,也可以使用一些在线小工具来达到转换:

在这里插入图片描述
  
  
  
  
  
  

6.2、快速构建

6.2.1、快速构建http请求和响应的报文格式

  1)、基本说明
  HTTP协议支持CS模式(即客户端服务器模式,也就是请求和响应模式:在一条通信线路上必定有一端是客户端,另一端是服务器端,请求从客户端发出,服务器响应请求并返回。)。
  HTTP协议是应用层协议,底层采用的是TCP,双方交互的是报文。
在这里插入图片描述

  
  
  
  2)、http请求和响应的报文格式

  简略图如下:
在这里插入图片描述

  
  说明1: http其逻辑层面的结构如上述,但实际是线性结构存储发送。(类似于磁盘的物理结构和逻辑结构)。
在这里插入图片描述
  
  说明2: 如何看待请求行、状态行中都存在协议版本?
  回答:客户端版本号和服务端需要匹配。客户端什么版本号,相应的服务端只能提供该版本能提供的服务。(举例:如我们使用的应用,若需要体验一些新功能,则需要把app升级,否则只能使用旧的版本号)
  
  说明3: HTTP协议如何封装和解包,向上交付?
  ①\r\n: 通过空行的方式,可区分报头和有效载荷。因此,把报头读完,接下来读取到的就是正文部分。
  ②正文的大小:当把报头读完时,其中存储了Cotent-Length,即可获取正文大小。这是一个 [key, value]模式结构,例如:[Cotent-Length,123],后面的123即本次正文的大小。
  
  
  
  
  

6.2.2、http demo

6.2.2.1、sock.hpp && httpserver.hpp

  
  sock.hpp:相关内容之前小节中讲述过,这里只是套用。

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Log.hpp"

class Sock
{
private:
    const static int gbacklog = 20; // listen中参数设置:详细将在后续介绍

public:
    // 构造
    Sock(){}; // 无参构造

    // 析构
    ~Sock(){};

    // 创建套接字:int socket(int domain, int type, int protocol);
    int Socket()
    {
        int listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock < 0)
        {
            logMessage(FATAL, "socket:创建套接字失败。%d:%s", errno, strerror(errno));
            exit(2); // 退出
        }
        logMessage(NORMAL, "socket:创建套接字成功, listensock:%d", listensock);
        return listensock; // 将套接字返回给TcpServer中的成员函数_listensock
    }

    // 绑定:int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    void Bind(int sock, uint16_t port, const std::string &ip = "0.0.0.0")
    {
        // 准备工作:sockaddr结构体
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);                    // 对端口号:需要转换为网络字节序
        inet_pton(AF_INET, ip.c_str(), &local.sin_addr); // 对ip:点分十进制风格-->网络字节序+四字节

        // 绑定:
        if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind:绑定失败。%d:%s", errno, strerror(errno));
            exit(3); // 退出
        }
        logMessage(NORMAL, "bind: 绑定成功。");
    }

    // 监听:int listen(int sockfd, int backlog);
    void Listen(int sock)
    {
        if (listen(sock, gbacklog) < 0)
        {
            logMessage(FATAL, "listen:监听失败。%d:%s", errno, strerror(errno));
            exit(4); // 退出
        }
        logMessage(NORMAL, "listen:监听成功。");
    }

    // 获取连接:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    int Accept(int listensock, std::string *ip, uint16_t *port) // 后两个*为输出型参数:这里的作用是将accept接收到的ip、port返回给TcpServer。
    {
        // 准备工作:用于接收源IP、源端口号
        struct sockaddr_in src;
        memset(&src, 0, sizeof(src));
        src.sin_family = AF_INET;
        socklen_t len = sizeof(src);

        // 获取连接
        int servicesock = accept(listensock, (struct sockaddr *)&src, &len);
        if (servicesock < 0)
        {
            logMessage(FATAL, "accept:接收失败。%d:%s", errno, strerror(errno));
            exit(5);
        }
        logMessage(NORMAL, "accept:接收成功。servicesock:%d", servicesock);
        if (ip)                            // 判空:获取源IP
            *ip = inet_ntoa(src.sin_addr); // 四字节+网络字节序--->主机字节序+点分十进制
        if (port)                          // 判空:获取源端口号
            *port = ntohs(src.sin_port);   // 网络字节序--->主机字节序

        return servicesock;
    }

    // int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    bool Connect(int sock, const uint16_t &port, const std::string &ip)
    {
        // 准备工作
        struct sockaddr_in aim;
        bzero(&aim, sizeof(aim));
        aim.sin_family = AF_INET;
        aim.sin_port = htons(port);                  // 主机字节序--->网络字节序
        aim.sin_addr.s_addr = inet_addr(ip.c_str()); // 主机字节序+点分十进制风格--->网络字节序+四字节
        // 连接
        if (connect(sock, (struct sockaddr *)&aim, sizeof(aim)) < 0)
        {
            logMessage(FATAL, "connect:连接失败。%d:%s", errno, strerror(errno));
            return false;
        }
        logMessage(NORMAL, "connect:连接成功。");
        return true;
    }
};

  
  
  httpserver.hpp:这里使用的是多进程版本。让子进程进行业务处理,与sock.hpp配合,将业务处理函数和服务端网络套接字部分解耦。

#pragma once
#include <iostream>
#include <signal.h>
#include <functional>
#include "Sock.hpp"

class HttpServer
{
public:
    using func_t = std::function<void(int)>;

    // 构造函数
    HttpServer() {}

    HttpServer(const uint16_t &port, func_t func)
        : port_(port), func_(func)
    {
        // 创建套接字
        listensock_ = sock_.Socket();
        // 绑定
        sock_.Bind(listensock_, port_);
        // 监听
        sock_.Listen(listensock_);
    }

    // 析构
    ~HttpServer()
    {
        if (listensock_ > 0)
            close(listensock_);
    }

    // 启动服务端:接收连接客户端,派子进程处理业务
    void Start()
    {
        signal(SIGCHLD, SIG_IGN); // 子进程在终止时会自动清理,不会产生僵尸进程,也不会通知父进程。

        for (;;)
        {
            std::string clientip;
            uint16_t clientport;

            int servicesock = sock_.Accept(listensock_, &clientip, &clientport); // 输出型参数
            if (servicesock < 0)
                continue;    // 本次连接失败,重新连接
            if (fork() == 0) // 子进程:执行业务处理
            {
                close(listensock_); // 关闭无用套接字
                func_(servicesock); // 业务处理函数
                close(servicesock); // 完成后关闭套接字并退出进行
                exit(0);
            }
            close(servicesock); // 父进程也要关闭对其无用的套接字
        }
    }

private:
    int listensock_; // 监听套接字
    uint16_t port_;  // 端口号
    func_t func_;    // 业务处理函数
    Sock sock_;
};

  
  
  
  
  

6.2.2.2、httpserver.cc:试着读取一个http请求并构建一个http的响应正文

  1)、演示代码
  相关代码如下:

#include <memory>
#include "HttpServer.hpp"

// 使用手册
void Usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " port\n"
              << std::endl;
}

// 业务函数
void HandlerHttpRequest(int sock)
{
    //1、读取一个http请求
    char buffer[10240];
    ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (s > 0)
    {
        buffer[s] = 0;
        std::cout << buffer << "--------------------\n"
                  << std::endl;
    }

    //2、返回一个http响应
    std::string httpresponse = "HTTP/1.1 200 OK\r\n";//状态行
    httpresponse += "\r\n";//此处暂时没有响应报头,故直接填\r\n
    httpresponse += "<html><h3> 飘风不终朝,骤雨不终日。\r\n </h3></html>";
    httpresponse += "<html><h3> my one and only.\r\n </h3></html>";
    send(sock, httpresponse.c_str(), httpresponse.size(), 0);

}

// 服务端启动: ./HttpServer port
int main(int argc, char **argv)
{
    // 命令行参数检查
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }

    // 创建服务器
    std::unique_ptr<HttpServer> server(new HttpServer(atoi(argv[1]), HandlerHttpRequest));
    // 启动服务器
    server->Start();

    return 0;
}

  
  
  
  2)、演示结果
  
  单纯读取一个http请求,结果如下:

// 业务函数
void HandlerHttpRequest(int sock)
{
    //1、读取一个http请求
    char buffer[10240];
    ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (s > 0)
    {
        buffer[s] = 0;
        std::cout << buffer << "--------------------\n"
                  << std::endl;
    }

}

  可以看到相关的请求报文格式:由于服务端没有进行响应,这里显示无法操作。
在这里插入图片描述

  大体介绍:可与之前的示意图对比理解。
在这里插入图片描述

  
  
  简单构建一个响应:

    //2、返回一个http响应
    std::string httpresponse = "HTTP/1.1 200 OK\r\n";//状态行
    httpresponse += "\r\n";//此处暂时没有响应报头,故直接填\r\n
    httpresponse += "<html><h3> 飘风不终朝,骤雨不终日。\r\n </h3></html>";
    httpresponse += "<html><h3> my one and only.\r\n </h3></html>";
    send(sock, httpresponse.c_str(), httpresponse.size(), 0);

  可以看到页面显示了我们的文本信息。

在这里插入图片描述

  
  
  
  
  

6.3、逐步细化

6.3.1、web根目录

6.3.1.1、基本说明

  说明1: 默认情况下,请求到的是web根目录,即/。如果想要访问某一资源,可输入具体的目录信息,如/a/b/c/d,表示为/a/b/c目录下的资源d。
在这里插入图片描述

  
  
  说明2: web根目录并非Linux的根目录,若有需要可以自定义位置设置web根目录所在路径。
  以下将进行相关演示:以wwwroot文件作为web根目录。

[wj@VM-4-3-centos http_demo]$ tree
.
|-- HttpServer
|-- HttpServer.cc
|-- HttpServer.hpp
|-- Log.hpp
|-- Makefile
|-- Sock.hpp
|-- Util.hpp
`-- wwwroot //web根目录
    |-- a
    |   `-- a-1.html //a目录下的资源
    |-- b
    |   `-- b-1.html //b目录下的资源
    |-- c
    `-- index.html //首页

4 directories, 10 files
[wj@VM-4-3-centos http_demo]$ 

  

6.3.1.2、访问一个静态网页:util.hpp、index.html

  在之前的演示中我们硬编码返回了一个响应,这里我们设置web根目录,并根据请求的url来打开相应目录下的资源,将其返回。
  如下,对业务处理函数做了一定修改,增添了1.2提取路径和1.3打开文件。

// 业务函数
void HandlerHttpRequest(int sock)
{
    // 1.1、读取一个http请求
    char buffer[10240];
    ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (s > 0)
    {
        buffer[s] = 0;
        std::cout << buffer << "--------------------\n"
                  << std::endl;
    }
    // 1.2、提取想要访问的资源路径
    std::vector<std::string> vline;
    Util::cutString(buffer, "\n", &vline); // 提取每一行(静态成员可直接用类访问)
    std::vector<std::string> vblock;
    Util::cutString(vline[0], " ", &vblock); // 提取第一行:将其每个部分分开

    std::string filepath = vblock[1]; // 请求行:方法 url http版本号
    std::string aimpath = WEBROOT;    // 固定web根目录./wwwroot
    if (filepath == "/")              // 说明访问web根目录./wwwroot/index.html
        aimpath += HOMEPAGE;
    else
        aimpath += filepath; //./wwwroot下的其它子目录
    std::cout << aimpath << std::endl;

    // 1.3、打开待访问路径下的文件
    std::string content;
    std::ifstream in(aimpath);
    if (in.is_open()) // 若成功打开,读取文件内容
    {
        std::string line;
        while (std::getline(in, line))
            content += line;
        in.close();
    }

    // 2、返回一个http响应
    std::string httpresponse;
    if (content.empty())
        httpresponse = "HTTP/1.1 404 NotFound\r\n"; // 状态行
    else
        httpresponse = "HTTP/1.1 200 OK\r\n"; // 状态行
    httpresponse += "\r\n";                   // 响应报头:此处暂时没有,故直接填\r\n
    httpresponse += content;                  // 正文
    send(sock, httpresponse.c_str(), httpresponse.size(), 0);
}

  
  这里路径提取使用的函数如下:

#pragma once

#include <iostream>
#include <vector>
#include <string>

class Util
{
public:
    // 以sep分割,从s中提取子串,存入out中(主要是为了获取url路径)
    // 例如: aaaaa\r\nbbbbbbb\r\nccc\r\n
    static void cutString(const std::string &s, const std::string &sep, std::vector<std::string> *out)
    {
        std::size_t start = 0;
        while (start < s.size())
        {
            // 寻找分隔符
            std::size_t pos = s.find(sep, start); // size_t find (const char* s, size_t pos = 0) const;
            if (pos == std::string::npos)// 没找到
                break;
            // 若找到,提取子串
            std::string sub = s.substr(start, pos - start); // string substr (size_t pos = 0, size_t len = npos) const;
            // std::cout << "----" << sub << std::endl;//测试代码:用于检验提取子串是否成功
            out->push_back(sub); // 找到后,将其逐一存入vector中
            // 跳过已经截取的子串长度及分隔符长度,继续寻找(此处不能使用Pos,因为其是下标位置,不是间距)
            start += (sub.size() + sep.size());
        }
        if (start < s.size()) // 说明还剩下字符
        {
            out->push_back(s.substr(start)); // 剩余的字符自成一行
        }
    }
};

  
  网页端使用的格式如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>【测试:这是index.html】</title>
</body>
    <h3>百年飘摇</h3>
    <p>ONE AND ONLY</p>

</body>
</html>

  
  
  以下为测试结果:
在这里插入图片描述
  
  
  
  
  
  

6.3.2、http的请求方法

6.3.2.1、基本说明

  常见HTTP方法如下:

方法说明支持的HTTP协议
GET获取资源1.0、1.1
POST传输实体主体1.0、1.1
PUT传输文件1.0、1.1
HEAD获得报文首部1.0、1.1
DELETE删除文件1.0、1.1
OPTIONS询问支持的方法1.1
TRACE追踪路径1.1
CONNECT要求用隧道协议连接代理1.1
LINK建立和资源之间的联系1.0
UNLINE断开连接关系1.0

  
  
  说明:我们平时的上网行为其实就两种
    a.从服务器端拿下来资源数据(GET方法)
    b.把客户端的数据提交到服务器(POST方法、GET方法)
  
  

6.3.2.2、在命令行中使用telnet命令测试GET方法

  例如:
在这里插入图片描述

  
在这里插入图片描述

  
  
  

6.3.2.3、使用表单来测试GET、POST方法

  1)、准备知识

  说明: HTML 表单用于收集用户的输入信息。HTML 表单表示文档中的一个区域,此区域包含交互控件,将用户收集到的信息发送到 Web 服务器。

  表单作用①: 通过交互控件收集用户数据,并把用户数据按照一定方法推送给服务器(即表单中的数据,会被转成http request的部分,在递交时指明提交方法:get、post等等)
  
  此时我们演示例子涉及文本域,其余表单用法可自行扩展学习。
在这里插入图片描述

  
  
  2)、测试:get方法和post方法
  根据上述简单演示一个html:
  action :用于指定提交表单数据的请求URL。
  method :表单数据发送至服务器的方法,常用的有两种, get(默认)/post。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GET方法和POST方法验证</title>
</head>

<body>
    <h3>测试: GEt && POST</h3>
    <form name="input" action="/index.html" method="GET">
        Username: <input type="text" name="user"> <br/>
        Password: <input type="password" name="pwd"> <br/>
        <input type="submit" value="登陆">
    </form>
</body>

</html>

  上述代码显示的网页信息如下:

在这里插入图片描述

  
  
  以下为相关验证结果:get方法通过url传参,回显输入的私密信息,不够私密。post方法通过正文提交参数,不会回显,一般私密性是有保证的。
  PS:私密不代表安全,它们都属于明文的方式。实际 对数据加密和解密才是安全

在这里插入图片描述
在这里插入图片描述
  
  
  
  3)、get和post的区别说明

指代不同:
get:从指定的资源请求数据。
post:向指定的资源提交要被处理的数据

规则不同:
get: 请求可被缓存;请求保留在浏览器历史记录中;请求可被收藏为书签;请求不应在处理敏感数据时 使用;请求有长度限制;请求应当用于获取数据。
post:请求不会被缓存;请求不会保留在浏览器历史记录中;不能被收藏为书签;请求对数据长度没有要求。

数据要求不同:
get:当发送数据时,get 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是2048 个字符)。与 post 相比,get 的安全性较差,因为所发送的数据是 URL 的一部分。
POST:发送数据无限制。post 比 get更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。

get请求的参数 url 可见,而 post 请求的参数 url 不可见。
post请求能发送更多的数据类型(get请求只能发送ASCII字符)。

  
  
  
  

6.3.3、http状态码

  1)、基本说明
  说明:常见的状态码,比如 200(OK)、404(Not Found)、403(Forbidden)、302(Redirect, 重定向)、504(Bad Gateway)。

类别原因短语
1XXInformational (信息性状态码)接收的请求正在处理
2XXSuccess (成功状态码)请求正常处理完毕
3XXRedirection (重定向状态码)需要进行附加操作以完成请求
4XXClient Error (客户端错误状态码)服务器无法处理请求
5XXServer Error (服务器错误状态码)服务器处理请求出错

  
  
  2)、举例介绍:3XX, 永久重定向 301 & 临时重定向 302、307
  相关参考文档:HTTP 的重定向。
  
  说明:在 HTTP 协议中,重定向操作由服务器向请求发送特殊的重定向响应而触发。重定向响应包含以 3 开头的状态码,以及 Location 标头,其保存着重定向的 URL。浏览器在接收到重定向时,它们会立刻加载 Location 标头中提供的新 URL。除了额外的往返操作中会有一小部分性能损失之外,重定向操作对于用户来说是不可见的。
在这里插入图片描述

  临时重定向: 不影响用户后续的请求策略。有时候请求的资源无法从其标准地址访问,但是却可以从另外的地方访问,在这种情况下,可以使用临时重定向。(举例:页面跳转,比如跳转到支付页面,完成后会跳转回主页)
  永久重定向: 影响用户后续的请求策略。这种重定向操作是永久性的,它表示原 URL 不应再被使用,而选用新的 URL 替换它。(举例:新老域名变更,老的域名不用了,可以让用户访问老的域名时,跳转到新的域名)
  
  
  演示代码:修改的只是httpserver.cc中的HandlerHttpRequest函数。如有需要也可以自定义一个.html文件

	// 2、返回一个http响应
    std::string httpresponse;
    if (content.empty())
    {
        httpresponse = "HTTP/1.1 302 Found\r\n"; // 状态行
        httpresponse += "Location: http://43.142.190.243:9090/b/b-1.html\r\n";//演示重定向到自己的网址
    }
    else
        httpresponse = "HTTP/1.1 200 OK\r\n"; // 状态行
    httpresponse += "\r\n";                   // 响应报头:此处暂时没有,故直接填\r\n
    httpresponse += content;                  // 正文
    send(sock, httpresponse.c_str(), httpresponse.size(), 0);

  演示结果:
在这里插入图片描述
在这里插入图片描述
  
  
  
  

6.3.4、HTTP常见Header(报头)

  1)、基本说明
  Content-Type: 数据类型(text/html等)
  Content-Length: Body的长度
  Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
  User-Agent: 声明用户的操作系统和浏览器版本信息;
  referer: 当前页面是从哪个页面跳转过来的;
  location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
  Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
  
  
  
  2)、相关演示
  演示代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTTP常见报头测试</title>
</head>

<body>
    <h3>测试:HTTP常见报头</h3>
    <form name="input" action="/index.html" method="GET">
        Username: <input type="text" name="user"> <br/>
        Password: <input type="password" name="pwd"> <br/>
        <input type="submit" value="登陆">
    </form>
    <img src="https://nie.res.netease.com/r/pic/20230524/4e16601a-0c79-4b2c-ac63-5bf637c1172c.png" alt="测试报头:添加图片" width="1722" height="950">
</body>

</html>

在这里插入图片描述
  
  如下为尝试添加的报头信息:

    // 2、返回一个http响应
    std::string httpresponse;
    if (content.empty())
    {
        httpresponse = "HTTP/1.1 302 Found\r\n"; // 状态行
        httpresponse += "Location: http://43.142.190.243:9090/b/b-1.html\r\n";//演示重定向到自己的网址
    }
    else
    {
        httpresponse = "HTTP/1.1 200 OK\r\n"; // 状态行
        httpresponse +=("Content-Type: text/html\r\n");// 报头:数据类型(text/html等) 
        httpresponse +=("Content-Length: " + std::to_string(content.size()) + "\r\n"); //报头:Body的长度
    }
    httpresponse += "\r\n";                   // 响应报头:此处暂时没有,故直接填\r\n
    httpresponse += content;                  // 正文
    send(sock, httpresponse.c_str(), httpresponse.size(), 0);

  演示结果如下:
在这里插入图片描述
  
  
  
  

6.3.5、会话管理(Cookie、Session)

  1)、问题引入
  问题说明:http具有简单快速、无连接、无状态的特征。其中,无状态指在HTTP协议中,服务器不会记录客户端的状态信息,每个请求都是独立的,服务器不会记住之前的请求信息,也被称为无状态传输协议(Stateless Transfer Protocol),这意味着服务器无法区分不同的请求是否来自同一个客户端,也无法知道客户端的历史状态。
  然而,当用户登录一个网站时,再次登录可以看到用户并未退出,原先的状态信息仍旧保存着。这是因为:虽然HTTP无状态,但浏览器一般会记录下相关网址的状态信息

  HTTP的两个报头属性:①Cookie,在请求中;②Set-Cookie,在响应中。

  
  
  2)、如何做到会话管理
  Cookie 访问服务器之后,服务器可以发送cookie给客户端,客户端默认就会保存这个cookie,下次自动发送。相关步骤简述如下:

1、服务端创建cookie对象,添加到响应中,发回给客户端。
2、客户端接收到响应,把cookie的数据保存“浏览器”中。
3、下次访问同一个“网站”时(服务器的服务),会自动携带cookie数据。
4、通过cookie的方式,实现了 “不同请求”之间数据“共享”。

在这里插入图片描述

  因此,提出在服务端存储用户的会话状态Session,并通过SessionID,来替代直接通过Cookie来维持用户的登录状态。

SessionID可被设置过期时间,攻击者将更难维持持久的登录状态(这也是为什么有时用户登录的状态信息,隔一段时间就会被退出,需要重新登录的原因)

SessionID实际不包含有意义的敏感信息,在诸如“需要原密码的密码重置”等场景,攻击者便难以简单地施展攻击。

在这里插入图片描述
  
  
  
  3)、相关演示
  1、简单演示如何查看网页中的cookie,以及移除相应cookie的结果:

在这里插入图片描述

  
  2、服务端响应时,返回cookie报头:

    // 2、返回一个http响应
    std::string httpresponse;
    if (content.empty())
    {
        httpresponse = "HTTP/1.1 302 Found\r\n"; // 状态行
        httpresponse += "Location: http://43.142.190.243:9090/b/b-1.html\r\n";//演示重定向到自己的网址
    }
    else
    {
        httpresponse = "HTTP/1.1 200 OK\r\n"; // 状态行
        httpresponse +=("Content-Type: text/html\r\n");// 报头:数据类型(text/html等) 
        httpresponse +=("Content-Length: " + std::to_string(content.size()) + "\r\n"); //报头:Body的长度
        httpresponse +=("Set-cookie: 这是一个cookie,用于验证测试\r\n");// 报头:Cookie,通常用于实现会话(session)的功能;
    }
    httpresponse += "\r\n";                   // 响应报头:此处暂时没有,故直接填\r\n
    httpresponse += content;                  // 正文
    send(sock, httpresponse.c_str(), httpresponse.size(), 0);

  结果如下:该 Cookie 成功被服务端(浏览器)接收到。
在这里插入图片描述

  当然,可以分为文件级的 Cookie 文件和内存级的 Cookie 文件。①文件级的 Cookie 文件是存储在用户计算机上的硬盘上,是一种持久性的 Cookie。它们的过期时间可以设置为一段时间,也可以永不过期。②内存级的 Cookie 文件是存储在内存中的临时 Cookie。当浏览器关闭时,它们会自动删除。
  
  
  
  

6.3.6、短连接与长连接

  1)、基本说明

Connection: keep-alive //长连接
Connection: close //短连接
优点缺点
长连接连接复用,每次请求耗时更短,性能更佳维护成本大:需要维护连接池,需要对连接进行保活、需要及时清理空闲时间过长的连接,需要动态维护连接池的大小
短连接实现简单,代码易于阅读和维护连接无法复用,请求耗时长,容易把下游服务的连接打满

  
  
  
  
  
  
  

7、HTTPS协议(应用层·三)

7.1、https是什么

  说明: HTTPS 也是⼀个应⽤层协议,其在 HTTP 协议的基础上,引⼊了⼀个加密层(TLS/SSL)

  
  问题一:HTTP与HTTPS有什么区别?
  HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,对比http协议要相对安全
在这里插入图片描述

  
  问题二:这里的安全是什么意思?
  此次并非指HTTPS协议是绝对安全的,只是说明其破解的成本远大于破解的收益。
  
  
  

7.2、预备知识

7.2.1、加密:是什么和为什么

  1)、什么是加密?

  加密:就是把 明文 (要传输的信息)进⾏⼀系列变换,⽣成 密文
  解密:就是把 密文 再进⾏⼀系列变换,还原成 明文
  在这个加密和解密的过程中,往往需要⼀个或者多个中间的数据辅助进⾏这个过程,这样的数据称为 密钥
在这里插入图片描述

  
  
  
  2)、为什么要加密?

  举例简述:http的内容是明⽂传输的,明⽂数据会经过路由器、wifi热点、通信服务运营商、代理服务器等多个物理节点,如果信息在传输过程中被劫持,传输的内容就完全暴露了。劫持者还可以篡改传输的信息且不被双⽅察觉,这就是 中间⼈攻击 ,所以我们才需要对信息进⾏加密。
  
  
  

7.2.2、常见加密方式(对称加密 && 非对称加密)

  1)、基本介绍
在这里插入图片描述

  
  
  
  

7.2.3、数据摘要(数据指纹) && 数字签名

  1)、数据摘要(数据指纹)
  概念: 数字指纹(数据摘要),其基本原理是利⽤单向散列函数(Hash函数)对信息进⾏运算,⽣成⼀串固定⻓度的数字摘要。数字指纹并不是⼀种加密机制,但可以⽤来判断数据有没有被窜改。
在这里插入图片描述

  摘要常见算法: 有MD5、SHA1、SHA256、SHA512等,算法把⽆限的映射成有限,因此可能会有碰撞(两个不同的信息,算出的摘要相同,但是概率⾮常低)
  
  以下为一个举例:
在这里插入图片描述

  
  
  2)、数字签名
  摘要经过加密,就得到数字签名。
  
  
  
  

7.3、HTTPS 的工作过程探究

7.3.1、方案 1:只使用对称加密

  方式说明:通信双⽅都各自持有同⼀个密钥X,使用该密钥加密、解密。这样,除非密钥被破解,否则除通信两⽅外无人知晓密钥情况,故可保障通信安全当然是可以被保证的。
在这里插入图片描述

  
  该方案存在的问题:

  1、维护成本高,这种情况下服务器需要维护每个客户端和每个密钥之间的关联关系。

  2、首次如何确保客户端和服务器共享密钥?
  比较理想的做法是:在客户端和服务器建立连接的时候,双方协商确定本次的密钥。但这种方案下,密钥的传输也必须加密传输,也就是先协商确定⼀个 “密钥的密钥”。如果对于密钥的传输仍旧使用对称加密,则会类似于盗梦空间一样无限套娃。
  
  
  

7.3.2、方案 2:只使用非对称加密

  方式说明:设服务器持有私钥,把公钥明文传输给客户端。
在这里插入图片描述
  
  
  该方案存在的问题:
  1、客户端—>服务器:经过公钥加密,服务器私钥解密,保证了安全;
  2、服务器—>客户端:经过私钥加密,客户端公钥解密,不能保证安全。(公钥被中间⼈劫持到,那他也能⽤该公钥解密服务器传来的信息)
  
  
  

7.3.3、方案 3:双方都使用非对称加密

  方式说明:服务端拥有公钥S与对应的私钥S’,客户端拥有公钥C与对应的私钥C’,客户和服务端交换彼此的公钥。
在这里插入图片描述

  此方案问题说明:
  1、效率太低
  2、依旧有安全问题
  
  
  
  

7.3.4、方案 4:非对称加密 + 对称加密

  方式说明: 由于对称加密的效率比非对称加密⾼很多,因此,①密钥协商阶段,使用非对称加密;②在数据通信阶段,使用对称加密。
在这里插入图片描述
  
  此方案问题说明:
  1、依旧有安全问题:以下为一个演示。
在这里插入图片描述

  
  
  
  

7.3.5、中间人攻击与CA证书认证流程

  1)、问题引入
  问题:在上述过程中,为什么中间人能够攻击成功?
在这里插入图片描述
  
  
  2)、认识CA认证
  1、CA,Catificate Authority,它的作用就是提供证书(即服务器证书,由域名、公司信息、序列号和签名信息组成)加强服务端和客户端之间信息交互的安全性,以及证书运维相关服务。任何个体/组织都可以扮演 CA 的角色,只不过难以得到客户端的信任,能够受浏览器默认信任的 CA 大厂商有很多。
  相关文章:细说 CA 和证书。

  
  2、服务端在使⽤HTTPS前,需要向CA机构申领⼀份数字证书,数字证书⾥含有证书申请者信息、公钥信息等。服务器把证书传输给浏览器,浏览器从证书⾥获取公钥就⾏了,证书就如⾝份证,证明服务端公钥的权威性。

在这里插入图片描述

  
  
  
  
  3)、理解数据签名(证书签发与验证)

  【对于证书签发】当服务端申请CA证书的时候,CA机构会对该服务端进行审核,并专门为该网站形成数字签名,过程如下:
  1. CA机构拥有非对称加密的私钥A和公钥A’
  2. CA机构对服务端申请的证书明文数据进行hash散列,形成数据摘要。
  3. 然后对数据摘要用CA私钥A’加密,得到数字签名S。服务端申请的证书明文和数字签名S共同组成了数字证书,这样⼀份数字证书就可以颁发给服务端了。

  示意图如下:
在这里插入图片描述

  【对于证书验证】当客户端获取到这个证书之后, 会对证书进行校验,防止证书伪造:
  1、判定证书的有效期是否过期。
  2、判定证书的发布机构是否受信任。PS:操作系统中已内置的受信任的证书发布机构。
  3、验证证书是否被篡改:从系统中拿到该证书发布机构的公钥,对签名解密,得到⼀个 数据摘要(设为 hash1)。然后相同算法计算整个证书的 hash 值(设为 hash2)。对比 hash1 和 hash2 是否相等, 如果相等,则说明证书是没有被篡改过的。
  
  
  
  
  

7.3.6、方案 5 : 非对称加密 + 对称加密 + 证书认证

  1)、基本说明
  方式说明: 在客户端和服务器刚⼀建立连接的时候,服务器给客户端返回⼀个证书,该证书包含了之前服务端的公钥,也包含了⽹站的⾝份信息。
在这里插入图片描述

  
  与方案4相比,数据通信阶段不变,仍旧采用对称加密。只是在密钥协商阶段引入了第三方CA机构,用于验证服务器身份信息,防止客户端与服务器直接通信时,由于不关心彼此身份而导致中间人钻篓子。

  第⼀组(非对称加密):用于校验证书是否被篡改。服务器在客户端请求后返回携带签名的证书,客户端通过CA的公钥进行证书验证,保证证书的合法性,进⼀步保证证书中携带的服务端公钥权威性。
  第二组(非对称加密):客户端用收到的CA证书中的公钥(是可被信任的)给随机生成的对称加密的密钥加密,传输给服务器,服务器通过私钥解密获取到对称加密密钥。
  第三组(对称加密): 客户端和服务器后续传输的数据都通过这个对称密钥加密解密。
  
  
  
  2)、常见问题
  问题一:中间人有没有可能篡改该证书?
  回答:若中间人篡改了证书的明⽂,由于他没有CA机构的私钥,所以无法hash之后用私钥加密形成签名,那么也就没法办法对篡改后的证书形成匹配的签名。
  如果强行篡改,客户端收到该证书后会发现明文和签名解密后的值不⼀致,则说明证书已被篡改,证书不可信,从而终止向服务器传输信息,防止信息泄露给中间⼈。
  
  
  问题二:中间人有没有可能掉包整个证书?
  回答:因为中间⼈没有CA私钥,所以⽆法制作假的证书。如果要想不被客户端识别到,中间⼈只能以自己的身份向CA申请证书进行掉包。这个确实能做到证书的整体掉包,但是由于证书明文中包含了域名等服务端认证信息,客户端依旧能够识别出来。
  
  
  
  

  
  
  
  
  
  

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

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

相关文章

vite环境变量相关

环境变量&#xff1a;根据环境的不同&#xff0c;灵活的自动读取相应的变量。避免了手动修改。 import path from path import postCssPxToRem from postcss-pxtorem import { defineConfig, loadEnv } from vite import createVitePlugins from ./vite/plugins import copy f…

【LeetCode刷题笔记】二叉树(三)

701. 二叉搜索树中的插入操作 解题思路: 1. 模拟 ,如果 根节点为空 ,就用 插入值创建根节点 直接返回。否则, cur 从 根节点 开始,比较 当前节点的值和插入值的大小关系 : 1)如果 插入值 < cur ,就

一张图系列 - “position_embedding”

关于位置编码&#xff0c;我感觉应该我需要知道点啥&#xff1f; 0、需要知道什么知识&#xff1f; multi head atten 计算 复数的常识 1、embedding 是什么&#xff1f; position embedding常识、概念&#xff0c;没有会怎样&#xff1f; 交换token位置&#xff0c;没有P…

vue手动搭建脚手架(保姆式教案)

目录 1.创建项目 1.node.js环境搭建 2.安装vue-cli 3.搭建项目 目录结构 1.创建项目 1.node.js环境搭建 下载安装node.js&#xff08;Download | Node.js&#xff09;&#xff0c;安装时不要安装在C盘Windowsr打开cmd管理工具开始输入命令检查node.js是否安装和版本号&a…

在IDEA中的DeBug调试技巧

一、条件断点 循环中经常用到这个技巧&#xff0c;例如&#xff1a;遍历1个List的过程中&#xff0c;想让断点停在某个特定值。 参考上图&#xff0c;在断点的位置&#xff0c;右击断点旁边的红点&#xff0c;会出来1个界面&#xff0c;在Condition这里填写断点条件即可&#…

QCustomPlot的下载和使用

0.QCustomPlot介绍 QCustomPlot是一个基于Qt画图和数据可视化的C控件。在Qt下的绘图工具有Qwt、QChart和QCustomPlot&#xff0c;置于选择哪个绘图工具各有优缺点。 在绘制大量数据&#xff08;10万个点以上&#xff09;时选择QCustomPlot&#xff0c;在数据量比较小时&#x…

docker内更新显卡cuda cudnn

当前docker使用的cuda为10.2&#xff0c;为保证服务器环境使用相同的cuda版本&#xff0c;需对cuda版本进行升级&#xff0c;时间长了忘记如何操作&#xff0c;此处记录一下&#xff1a; *docker内使用的cuda版本低于容器外的显卡驱动版本即可&#xff0c;此处不对显卡驱动进行…

解决STM32F429烧录程序后还需复位才能植入程序的bug

1.打开魔术棒&#xff0c;打开debug 2.打开setting 3.打开Flas Download 4.开启Reset and Run 5.点进去Pack选项页面&#xff0c;去掉enable

springboot 2.1.0.RELEASE 项目加入swagger接口文档

Release v2.1.0.RELEASE spring-projects/spring-boot GitHub springboot 2.1.0.RELEASE发行日期是2018年10月30日&#xff08;Oct 30, 2018&#xff09; 不要使用过高的swagger版本&#xff0c;如SpringFox Boot Starter 3.0.0&#xff0c;否则报错&#xff1a; spring-…

MOS管体电极接源端版图layout画法

记录一个lvs一直跑不通的问题。 问题描述&#xff1a;lvs一直显示某几个MOS管的体电极连接问题。连线没有问题&#xff0c;版图中已经画了衬底。 原因&#xff1a; 图中四个管子的衬底接了源端&#xff0c;没接电源。 解决办法&#xff1a; 法1、源端接地 法2、将这四个管子…

msvcp140.dll丢失的解决办法,msvcp140.dll丢失会导致电脑出现哪些错误

msvcp140.dll丢失的解决办法都有哪些&#xff1f;如果电脑不及时将msvcp140.dll文件进行修复的话电脑可能会出电脑可会出现哪些错误&#xff1f;今天就和大家说说都有哪些解决办法有效的解决msvcp140.dll丢失的问题。 一.缺失msvcp140.dll会有什么问题 当电脑上缺少msvcp140.d…

67基于matlab图像处理,包括颜色和亮度调整、翻转功能、空间滤波和去噪、频域滤波和去噪、噪声添加,形态学操作、边缘检测及示波器集成的GUI图像处理。

基于matlab图像处理&#xff0c;包括颜色和亮度调整、翻转功能、空间滤波和去噪、频域滤波和去噪、噪声添加&#xff0c;形态学操作、边缘检测及示波器集成的GUI图像处理。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 67 matlab图像处理图像降噪 (xiaohon…

Python Web APP在宝塔发布

本地测试运行&#xff1a;uvicorn main:app --host 127.0.0.1 --port 8082 --reload 宝塔发布&#xff1a; 运行配置——>启动模式&#xff1a;worker_class uvicorn.workers.UvicornWorker

stable diffusion到底是如何工作的

stable diffusion简单入门 stable diffusion是一个文生图模型&#xff0c;主要由CompVis、Stability AI和LAION的研究者们创建。这个模型主要是在512X512分辨率的图像上训练的&#xff0c;训练数据集是LAION-5B&#xff0c;该数据集是目前可访问的最大的多模态数据集。 在这篇…

Spring6(四):JUnit、事务

文章目录 5. 单元测试&#xff1a;JUnit5.1 整合JUnit55.2 整合JUnit4 6. 事务6.1 JdbcTemplate6.1.1 准备工作6.1.2 实现CURD①装配 JdbcTemplate②测试增删改功能③查询数据返回对象④查询数据返回list集合⑤查询返回单个的值 6.2 事务6.2.1 编程式事务6.2.2 声明式事务 6.3 …

【数据结构】直接选择排序(你知道最不常用的排序算法有哪些吗?)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有帮助…

AI创作系统ChatGPT网站源码+详细搭建部署教程+支持DALL-E3文生图/支持最新GPT-4-Turbo-With-Vision-128K多模态模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

OpenSign:安全可靠的电子签名解决方案 | 开源日报 No.76

microsoft/Web-Dev-For-Beginners Stars: 71.5k License: MIT 这个开源项目是一个为期 12 周的全面课程&#xff0c;由微软云倡导者团队提供。它旨在帮助初学者掌握 JavaScript、CSS 和 HTML 的基础知识。每一节都包括预习和复习测验、详细的书面指南、解决方案、作业等内容。…

JavaScript学习_01——JavaScript简介

JavaScript简介 JavaScript介绍 JavaScript是一种轻量级的脚本语言。所谓“脚本语言”&#xff0c;指的是它不具备开发操作系统的能力&#xff0c;而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式&#xff08;embedded&#xff09;语言。它本身提供…

第三篇 《随机点名答题系统》——人员管理详解(类抽奖系统、在线答题系统、线上答题系统、在线点名系统、线上点名系统、在线考试系统、线上考试系统)

目录 1.功能需求 2.数据库设计 3.流程设计 4.关键代码 4.1.人员分组 4.1.1数据请求示意图 4.1.2添加组别&#xff08;login.php&#xff09;数据请求代码 4.1.3编辑组别&#xff08;login.php&#xff09;数据请求代码 4.1.4加入分组&#xff08;login.php&#xff09…
最新文章