超文本传输协议HTTP

news2024/11/18 1:27:46

HTTP协议

在网络通信中,我们可以自己进行定制协议,但是也有许多已经十分成熟的应用层协议,比如我们下面说的HTTP协议。

HTTP协议简介

HTTP(Hyper Text Transfer Protocol)协议又叫做超文本传输协议,是一个简单的请求-响应协议,HTTP通常运行在TCP之上,它是一个应用层协议。

URL

URL(Uniform Resource Lacator)叫做统一资源定位符,也就是我们通常所说的网址,是因特网的万维网服务程序上用于指定信息位置的表示方法。

一个URL由一下几部分组成。

http://user:pass@www.example.jp:80/dir/index.htm?uid=1#ch1
  • [http://]

一、协议方案名

http://代表的是协议方案名,表示请求时需要用到的协议,通常使用的是HTTP协议或者安全协议HTTPS。HTTPS协议是以安全为目标的HTTP协议,即在HTTP协议的基础上通过传输加密和身份认证保证了传输过程的安全性。

常见的应用层协议还有:DNS协议:域名系统、FTP协议:文件传输协议、TELNET协议:远程终端协议、HTTPS协议:安全数据传输协议、SMTP协议:电子邮件传输协议、POP3协议:邮件读取协议、SNMP协议:简单网络管理协议、TFTP协议:简单文件传输协议。

  • [user:pass]

二、登录信息(认证)

user:pass代表的是登录认证信息,包括用户的用户名和密码,虽然登录信息可以在URL中体现出来,但是绝大多数URL这个字段都是被省略的,因为登录信息可以通过其他方案交付给服务器。

  • [www.example.jp]

三、服务器地址

www.example.jp表示的是服务器地址,也叫做域名,比如www.baidu.comwww.qq.comwww.taobao.com等。

要注意的是,我们使用IP地址标识公网内的一台主机,但是IP地址本身不适合给用户看。我们可以通过ping命令,获得www.baidu.com域名解析后的IP地址。

如果用户看到的是IP地址,用户在访问网站前就不知道这个网站到底是干什么的,但是如果用户看到的是www.baidu.com这个域名,那么用户就至少知道这个网站是哪个公司的,因此域名有更好的自描述性。

实际上,我们可以认为域名和IP地址是等价的,因为在计算机中既可以使用域名,也可以使用IP地址。但是URL呈现出来的是可以让用户看到的,因此URL是以域名形式代表服务器地址的。

  • [80]

四、服务器端口号

80代表的是服务端口号,HTTP协议和套接字编程是一样位于应用层的,在进行套接字编程时,我们给服务器绑定对应的IP和端口号,而应用层协议同样需要明确的端口号。

常见协议对应的端口号:

HTTP-80、HTTPS-443、SSH-22

我们在使用某个协议时,该协议就是在给我们提供服务。现在大部分的服务与端口号是对应的,所以我们在使用时不用指明某个协议对应的端口号,因此在URL中服务器的端口号也是被省略的。

  • [/dir/index.htm]

五、带层次的文件路径

/dir/index.htm代表的是要访问的资源所在的路径,访问服务器是为了获取服务器上的某个资源,通过前面的域名和端口号已经可以找到对应的进程了,此时要做的就是指明该资源所在的路径。

比如我们输入百度的域名,浏览器就帮我们获取了百度的首页。

image-20240330144435887

当我们发起网页请求时,本质是获得了一张网页信息,然后浏览器对这张网页信息进行解释,最后就呈现出了对应的网页。

我们把这种资源称为网页资源,此外我们还可以向服务器请求视频、音频、图片等资源。HTTP之所以叫超文本传输协议,是因为很多资源并不是普通的文本资源。

因此在URL当中就有这样一个字段,用于表示要访问的资源所在的路径。此外我们可以看到,这里的路径分隔符是/,而不是\,这也就证明了实际很多服务都是部署在Linux上的。

  • [uid=1]

六、查询字符串

uid=1代表的是请求时提供的额外参数、这些参数是以键值对的形式存在的,通过&分隔。

我们在百度中搜索腾讯,URL中的参数其中一个是wd=腾讯,这个参数wd就是word,表示我们搜索的关键字。

image-20240330145236704

因此双方在网络通信时,是可以通过URL传输数据给用户的。

  • [ch1]

七、片段标识符

chi1代表的是片段标识符,是对资源的部分补充。

我们在看一组图片时,URL中就会出现片段标识符。

urlencode和urldecode

如果在搜索关键字当中出现了像/?:这样的字符,由于这些字符已经被URL当作特殊意义理解了,因此URL在呈现时会对这些特殊字符进行转义。

转义规则:

将需要转码的字符转换为16进制,从右到左,取4位(不足4位直接处理),每两位做一位,前面加上%,编码成%XY格式。

比如我们搜索C//时,由于/是一个特殊符号,就会被转义成对应的16进制的值0x2F,一个/就是%2F

image-20240330150940549

注意:URL不仅会对这些特殊符号做编码,也会对中文进行编码。

我们通过在线编码解码工具,看看我们上面说的对不对。

输入C%2F%2F

image-20240330151347651

点击解码

和我们在刚刚百度输入的一样

image-20240330151432728

实际上在服务器拿到对应的URL后,也需要对编码后的参数进行解码。

解码其实就是编码的逆过程。

HTTP协议格式

HTTP协议请求格式

image-20240330152611135

HTTP请求由以下四部分组成

  • 请求行

[请求方法]+[URL]+[http版本]

  • 请求报头

请求的属性,一般以[key:value]的形式,以行为单位陈列。

  • 空行

遇到空行代表请求报头结束。

  • 请求正文

允许为空,如果不为空,在请求报头中会有一个Content-Length属性来标识请求正文长度。

前三部分是HTTP协议自带的,请求正文一般是用户的信息或数据,如果没有信息上传就为空。

分离HTTP请求的报头和有效载荷

当应用层收到一个HTTP请求时,它必须将HTTP的报头和有效载荷进行分离。

请求行和请求报头就是HTTP报头,请求正文就是HTTP的有效载荷。

我们可以通过HTTP请求中的空行来进行分离报头和有效载荷。当服务器收到一个HTTP请求,就可以进行按行读取,如果读取到空行,就说明已经把报头读取完了。

实际上,HTTP请求中的空行就是为了方便分离报头和有效载荷的。

如果我们把HTTP请求想象成一个大的线性结构,每行都是用\n隔开的,如果连续读到两个\n就说明已经把报头读取完毕了,剩下的就是有效载荷了。

尝试获取浏览器的HTTP请求

在网络协议栈中,应用层的下一层叫做传输层,而HTTP协议底层通常使用的传输层协议是TCP协议,因此我们可以用套接字编写一个TCP服务器,然后启动浏览器访问我们的这个服务器。

由于我们的服务器是直接用TCP套接字读取浏览器发来的HTTP请求,此时在服务端没有应用层对这个HTTP请求进行过任何解析,因此我们可以直接将浏览器发来的HTTP请求进行打印输出,此时就能看到HTTP请求的基本构成。

我们编写一个简单的TCP服务器,这个服务器要做的就是把浏览器发来的HTTP请求进行打印即可。

#include <iostream>
#include <cassert>
#include <fstream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;

class Socket {
private:
    int Create() {
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sock < 0){
            cerr << "socket error!" << endl;
            return 1;
	    }
        return 0;
    }
    int Bind() {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(8081);
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
            cerr << "bind error!" << endl;
            return 2;
        }
        return 0;
    }
    int Listen() {
        if (listen(_listen_sock, 5) < 0){
            cerr << "listen error!" << endl;
            return 3;
	    }
        return 0;
    }
public:
    Socket():_listen_sock(-1){}
    void initServer() {
        //1.创建TCP套接字
        assert(Create() == 0);
        //2.bind绑定自己的网络信息
        assert(Bind() == 0);
        //3.设置服务器的socket为监听状态,监听客户端什么时候发来连接请求
        assert(Listen() == 0);
    }
    void start() {
        struct sockaddr peer;
        memset(&peer, 0, sizeof(peer));
        socklen_t len = sizeof(peer);
        for(;;) {
            int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
            if(sock < 0) {
                cerr << "accept err" << endl;
                continue;
            }
            if(fork() == 0) {
                //爸爸进程
                close(_listen_sock);
                if(fork() > 0) {
                    exit(0);
                }
                //孙子进程
                char buffer[1024];
                recv(sock, buffer, sizeof(buffer), 0);//读取HTTP请求
                cout << "--------------------------http request begin--------------------------" << endl;
                cout << buffer << endl;
                cout << "---------------------------http request end---------------------------" << endl;
                close(sock);
                exit(0);
            }
            //爷爷进程
            close(sock);
            waitpid(-1, nullptr, 0);//等待爸爸进程
        }
    }
    
private:
    int _listen_sock;
};

int main() {
    Socket sock;
    sock.initServer();
    sock.start();
    return 0;
}

image-20240330163438992

运行服务器程序后,然后用浏览器进行访问,此时我们的服务器就会收到浏览器发来的HTTP请求,并将收到的HTTP请求进行打印输出。

注意:

  • 浏览器向我们的服务器发起HTTP请求后,因为我们的服务器没有对进行响应,此时浏览器就会认为服务器没有收到,然后再不断发起新的HTTP请求,因此虽然我们只用浏览器访问了一次,但会受到多次HTTP请求。

  • 浏览器发起请求时默认用的就是HTTP协议,因此我们在浏览器的url框当中输入网址时可以不用指明HTTP协议。

  • url当中的/不能称之为我们云服务器上根目录,这个/表示的是web根目录,这个web根目录可以是你的机器上的任何一个目录,这个是可以自己指定的,不一定就是Linux的根目录。

其中请求行一般是不携带域名以及端口号的,因为在请求报头中的Host字段会进行指明,请求行当中的url表示你要访问这个服务器上的哪一路径下的资源。

image-20240330152611135

如果浏览器在访问我们的服务器时指明要访问的资源路径,那么此时浏览器发起的HTTP请求当中的url也会跟着变成该路径。

image-20240330164317720

请求报头当中全部都是以key: value形式按行陈列的各种请求属性,请求属性陈列完后紧接着的就是一个空行,空行后的就是本次HTTP请求的请求正文,此时请求正文为空字符串,因此这里有两个空行。

HTTP的响应格式

image-20240330165030649

HTTP响应由以下四部分组成:

  • 状态行:

[http版本]+[状态码]+[状态码描述]

  • 响应报头

响应的属性,这些属性都是以key: value的形式按行陈列的。

  • 空行

遇到空行表示响应报头结束。

  • 响应正文

响应正文允许为空字符串,如果响应正文存在,则响应报头中会有一个Content-Length属性来标识响应正文的长度。比如服务器返回了一个html页面,那么这个html页面的内容就是在响应正文当中的。

分离HTTP响应的报头和有效载荷

状态行和响应报头就是HTTP的报头信息,响应正文实际就是HTTP的有效载荷。

与HTTP请求相同,当应用层收到一个HTTP响应时,也是根据HTTP响应当中的空行来分离报头和有效载荷的

当客户端收到一个HTTP响应后,就可以按行进行读取,如果读取到空行则说明报头已经读取完毕。

构建一个HTTP响应

我们就将当前服务程序所在的路径作为我们的web根目录,我们可以在该目录下创建一个html文件,然后编写一个简单的html作为当前服务器的首页。

<html>
    <head></head>
    <body>
        <h1>hello http</h1>
    </body>
</html>
#include <iostream>
#include <cassert>
#include <fstream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;

class Socket {
private:
    int Create() {
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sock < 0){
            cerr << "socket error!" << endl;
            return 1;
	    }
        return 0;
    }
    int Bind() {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(8081);
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
            cerr << "bind error!" << endl;
            return 2;
        }
        return 0;
    }
    int Listen() {
        if (listen(_listen_sock, 5) < 0){
            cerr << "listen error!" << endl;
            return 3;
	    }
        return 0;
    }
public:
    Socket():_listen_sock(-1){}
    void initServer() {
        //1.创建TCP套接字
        assert(Create() == 0);
        //2.bind绑定自己的网络信息
        assert(Bind() == 0);
        //3.设置服务器的socket为监听状态,监听客户端什么时候发来连接请求
        assert(Listen() == 0);
    }
    void start() {
        struct sockaddr peer;
        memset(&peer, 0, sizeof(peer));
        socklen_t len = sizeof(peer);
        for(;;) {
            int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
            if(sock < 0) {
                cerr << "accept err" << endl;
                continue;
            }
            if(fork() == 0) {
                //爸爸进程
                close(_listen_sock);
                if(fork() > 0) {
                    exit(0);
                }
                //孙子进程
                char buffer[1024];
                recv(sock, buffer, sizeof(buffer), 0);//读取HTTP请求
                cout << "--------------------------http request begin--------------------------" << endl;
                cout << buffer << endl;
                cout << "---------------------------http request end---------------------------" << endl;
                close(sock);
                exit(0);
            }
            //读取default.html文件
			ifstream in("default.html");
			if (in.is_open()){
				in.seekg(0, in.end);
				int len = in.tellg();
				in.seekg(0, in.beg);
				char* file = new char[len];
				in.read(file, len);
				in.close();
				
				//构建HTTP响应
				string status_line = "http/1.1 200 OK\n"; //状态行
				string response_header = "Content-Length: " + to_string(len) + "\n"; //响应报头
				string blank = "\n"; //空行
				string response_text = file; //响应正文
				string response = status_line + response_header + blank + response_text; //响应报文
				
				//响应HTTP请求
				send(sock, response.c_str(), response.size(), 0);

				delete[] file;
            }
            //爷爷进程
            close(sock);
            waitpid(-1, nullptr, 0);//等待爸爸进程
        }
    }
private:
    int _listen_sock;
};

int main() {
    Socket sock;
    sock.initServer();
    sock.start();
    return 0;
}

当浏览器向服务器发起HTTP请求时,不管浏览器发来的是什么请求,我们都将这个网页响应给浏览器,此时这个html文件的内容就应该放在响应正文当中,我们只需读取该文件当中的内容,然后将其作为响应正文即可。

image-20240330173614981

我们也可以通过telnet命令来访问我们的服务器,也是能够得到这个HTTP响应的。

image-20240330174003655

注意:

  • 实际我们在进行网络请求的时候,如果不指明请求资源的路径,此时默认你想访问的就是目标网站的首页,也就是web根目录下的index.html文件。
  • 我们在构建HTTP响应时,在响应报头当中只添加了一个属性信息Content-Length,表示响应正文的长度,实际HTTP响应报头当中的属性信息还有很多。
为什么在进行通信时,要交互双方的版本

HTTP请求当中的请求行和HTTP响应当中的状态行,当中都包含了http的版本信息。

HTTP请求是由客户端发的,因此HTTP请求当中表明的是客户端的http版本,而HTTP响应是由服务器发的,因此HTTP响应当中表明的是服务器的http版本。

在通信时交互版本,目的是为了兼容性。因为客户端和服务端使用的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

UNLINK:

  • 作用:断开连接关系
  • 对应版本:1.0

这些方法中,最常用的就是GET和POST方法。

GET方法一般用于获取某种资源信息,POST方法一般用于将数据上传给服务器。

实际上传数据时,也有可能使用GET方法。

GET和POST方法都是可以传参的:

  • GET通过URL传参
  • POST通过正文传参

由于URL的长度是有限的,所以POST方法通过正文传参能传递更多的参数。

而且理论上使用POST方法更加安全,因为POST方法不会把你的参数回显到URL中,也就不会被人轻易看到。

实际上,POST和GET方法都是不安全的,要做到安全智能通过加密来实现。

演示GET和POST的不同

我们html中再添加两个表单,作为用户名和密码的输入。

image-20240330180515731

效果:

image-20240330180607858

我们使用GET方法,当我们提交完用户名和密码时,我们的用户名和密码就会自动被同步到URL当中。

image-20240330180501109

服务器也能收到提交的请求。

image-20240330180207365

如果我们改为POST方法,我们提交的参数就不会在URL中显示。

image-20240330180854247

服务器会通过正文来接收到用户名和密码

image-20240330181031099

注意:

  • 我们使用GET方法时,会将提交的参数提交到URL中,所以GET方法一般是处理数据不敏感的
  • 如果你传递的一些数据比较私密,那就可以使用POST方法,不是因为POST安全,而是因为它通过正文传输数据,不会把数据回显到URL中,相对来说比较私密。
HTTP的状态码

1XX:信息性状态码,指接收的请求正在处理

2XX:成功状态码,请求正常处理完毕

3XX:重定向状态码,需要进行附件操作以完成请求

4XX:客户端错误状态码,服务器无法处理请求

5XX:服务器错误状态码,服务器处理请求出错

常见的状态码:200(OK)、302(Redirect)、404(Not Found)、403(Forbidden请求权限不够)、504(Bad Gateway)

重定向

重定向,也称为 URL 转发,通过各种方法将各种网络请求重新定个方向转到其它位置,这个服务器相当于提供了一个引路的服务。

重定向又可分为临时重定向和永久重定向,其中状态码301表示的就是永久重定向,而状态码302和307表示的是临时重定向。

临时重定向和永久重定向本质是影响客户端的标签,决定客户端是否需要更新目标地址。

如果某个网站是永久重定向,那么第一次访问该网站时由浏览器帮你进行重定向,但后续再访问该网站时就不需要浏览器再进行重定向了,此时你访问的直接就是重定向后的网站。

而如果某个网站是临时重定向,那么每次访问该网站时如果需要进行重定向,都需要浏览器来帮我们完成重定向跳转到目标网站。
演示重定向

#include <iostream>
#include <cassert>
#include <fstream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;

class Socket {
private:
    int Create() {
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sock < 0){
            cerr << "socket error!" << endl;
            return 1;
	    }
        return 0;
    }
    int Bind() {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(8080);
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
            cerr << "bind error!" << endl;
            return 2;
        }
        return 0;
    }
    int Listen() {
        if (listen(_listen_sock, 5) < 0){
            cerr << "listen error!" << endl;
            return 3;
	    }
        return 0;
    }
public:
    Socket():_listen_sock(-1){}
    void initServer() {
        //1.创建TCP套接字
        assert(Create() == 0);
        //2.bind绑定自己的网络信息
        assert(Bind() == 0);
        //3.设置服务器的socket为监听状态,监听客户端什么时候发来连接请求
        assert(Listen() == 0);
    }
    void start() {
        struct sockaddr peer;
        memset(&peer, 0, sizeof(peer));
        socklen_t len = sizeof(peer);
        for(;;) {
            int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
            if(sock < 0) {
                cerr << "accept err" << endl;
                continue;
            }
            if(fork() == 0) {
                //爸爸进程
                close(_listen_sock);
                if(fork() > 0) {
                    exit(0);
                }
                //孙子进程
                char buffer[1024];
                recv(sock, buffer, sizeof(buffer), 0);//读取HTTP请求
                cout << "--------------------------http request begin--------------------------" << endl;
                cout << buffer << endl;
                cout << "---------------------------http request end---------------------------" << endl;
                close(sock);
                exit(0);
            }
            //构建HTTP响应
			string status_line = "http/1.1 307 Temporary Redirect\n"; //状态行
			string response_header = "Location: https://www.qq.com/\n"; //响应报头
			string blank = "\n"; //空行
			string response = status_line + response_header + blank; //响应报文
			
			//响应HTTP请求
			send(sock, response.c_str(), response.size(), 0);
            //爷爷进程
            close(sock);
            waitpid(-1, nullptr, 0);//等待爸爸进程
        }
    }
private:
    int _listen_sock;
};

int main() {
    Socket sock;
    sock.initServer();
    sock.start();
    return 0;
}

我们运行程序后,在浏览器上进行访问,我们会发现他自己跳转到了ww.qq.com

我们再用telnet演示:

image-20240330182826757

HTTP常见的Header

常见的Header:

  • Content-Type:数据类型(text/html等)。
  • Content-Length:正文的长度。
  • Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。

Host字段代表了客户端要访问的服务的IP和端口。我们在通过浏览器访问我们的服务器时,HTTP请求中的Host字段填的就是服务器的IP和端口。

为什么客户端还要告诉服务器它要访问的服务的对应的IP和端口?

因为有的服务器是一个代理服务器,它是替代客户端向其他服务器发起请求,然后把请求的结果返回给客户端。这种情况下,就需要告诉代理服务器,它需要访问的IP和端口。

  • User-Agent:声明用户的操作系统和浏览器的版本信息。

User-Agent代表的是客户端对应的操作系统和浏览器的版本信息。

image-20240330184038281

  • Referer:当前页面是哪个页面跳转过来的。

代表从哪个页面跳转过来的,方便回退到上一个网页。

  • Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。

  • Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能。

  • Connect:Keep-Alive长连接

HTTP1.0的常见工作方式是客户端和服务端先建立连接,然后客户端再给服务端发起请求,服务器再进行响应。

如果一个连接建立以后客户端和服务端只进行一次交互,就将连接关闭,那就太浪费资源了,所以主流的HTTP1.1是支持长连接的。

长连接就是,建立连接后,客户端可以向服务端不断的写入多个HTTP请求,而服务器在上层一个个读取这些请求就行了,此时一条连接就可以传送大量的请求和响应。

如果HTTP请求或者响应报头中Connect字段对应的值是Keep-Alive,就代表是支持长连接的。

Cookie和Session

Cookie

HTTP实际上是一种无状态协议,HTTP的每次请求和响应之间是没有任何关系的,但是你在使用浏览器时发现并不是这样的。

比如,你登录了一次csdn后,就算你把浏览器关闭或者重启了电脑,当再一次打开csdn时,csdn并没有要求你重新登录,这实际就是通过Cookie实现的。

点击edge浏览器的锁标志就可以查看网站对应的Cookie数据。

image-20240330191837755

这些cookie数据都是服务器方写的,如果你将某些cookie删除,那么此时可能就需要你重新进行登录认证了,因为你删除的可能正好就是你登录时所设置的cookie信息。

那么什么是Cookie?

HTTP是一种无状态协议,如果没有Cookie,那么我们每次在访问页面时都需要进行身份认证也就是登录账号。

比如,你是某视频网的会员,当你访问各种VIP视频时,都要麻烦你登录一次,这样就是让人感觉体验极差。

而Cookie就是为了解决这个问题的:

我们在第一次登录这个网站时,进行了身份认证,服务器确认你是一个合法的用户,就会进行Set-Cookie操作。

注意:Set-Cookie是HTTP报头中的一种属性信息

在服务器Set-Cookie后,服务器进行HTTP响应时就会把这个Set-Cookie响应给浏览器,浏览器在收到响应后,会将Set-Cookie中的值提取出来放到本地的Cookie文件中,此时就相等于把用户名和密码保存在了本地。

image-20240330193618243

往后只要再次访问这个网站,浏览器会再次发起http请求,同时将保存的cookie信息推送至服务器,无须用户再次输入账号密码重新登录。
cookie信息可分为文件级cookie和内存级cookie两种,在本地,Cookie是可以手动配置文件级或者内存级。

如果是内存级cookie,则cookie的生命周期随浏览器进程,当我们把浏览器关闭之后,cookie信息会自动销毁,重新打开浏览器登录网站时,则需要再次输入账号和密码。

如果是文件级cookie,则cookie不受浏览器关闭的影响,或电脑开关机重启的影响,因为他存在于磁盘外设上。

但是Cookie还存在一个被盗用的问题。

Cookie中存储的是我们的私密信息,一旦Cookie被盗,我们的信息也就泄露了。因此主流的服务器还引入了一个SessionID。

Session

当我们第一次登录一个网站时,服务器认证成功后还会生成一个对应SessionID,这个SessionID和用户的信息是不相关的。系统会把所有登录用户的SessionID维护起来。

此时当认证通过后服务端在对浏览器进行HTTP响应时,就会将这个生成的SessionID值响应给浏览器。

浏览器收到响应后会自动提取出SessionID的值,将其保存在浏览器的cookie文件当中。后续访问该服务器时,对应的HTTP请求当中就会自动携带上这个SessionID。

image-20240330194945125

服务器识别到HTTP请求当中包含了SessionID,就会提取出这个SessionID,然后再到对应的集合当中进行对比,对比成功就说明这个用户是曾经登录过的,此时也就自动就认证成功了,然后就会正常处理你发来的请求,这就是我们当前主流的工作方式。

  • 安全是相对的

引入SessionID后,浏览器中的Cookie文件中存的是SessionID,但是这个SessionId同样可以被盗取。

但是此时用户的用户名和密码就不会泄露了,由于SessionID已经泄露,非法用户仍然可以通过SessionID区访问我们曾经登录过的服务器,还是存在刚才的问题。

虽然引入SessionID并没有彻底解决安全问题,但是这个方法是相对安全的。因为互联网上是不存在绝对的安全的,网络中的信息随时是可能被别人破解的。

但是如果破解成本远大于破解带来的收益,那么就不会有人去破解这个信息了,这个信息就可以说是安全的。

引入SessionID的好处

引入SessionID后,用户的用户名和密码都是由服务器来维护的,本地Cookie文件中存的只是SessionID。

虽然SessionID有可能被盗取,但是服务器也有一些策略来保证用户信息的安全性。

  1. 服务器可以通过IP地址来判断用户登录的地址范围,如果用户在短时间内登录地址发生了巨大变化,此时服务器就会知道这个用户出异常了,就会清楚服务器中对应的SessionID。

这会要求用户在登录时重新输入用户名和密码,因为用户名和密码只有本人和服务器知道。重新验证后,就会将另一个IP识别为非法用户,加入黑名单中。将合法用户和非法用户分为白名单和黑名单。

  1. 当用户进行一些高权限的操作时,会让用户再次输入密码,再次确认信息。

非法用户是不知道正确的密码的,就会让非法用户不能进行高权限的操作。比如修改密码时,需要输入旧密码。

  1. SessionID也是会过期的,假如某一个SessionID只有1小时的有效期。

即使你的SessionID被盗用了,也仅仅是在1小时内被盗用,而且和权限也有限,因此影响不会太大。

理你发来的请求,这就是我们当前主流的工作方式。

  • 安全是相对的

引入SessionID后,浏览器中的Cookie文件中存的是SessionID,但是这个SessionId同样可以被盗取。

但是此时用户的用户名和密码就不会泄露了,由于SessionID已经泄露,非法用户仍然可以通过SessionID区访问我们曾经登录过的服务器,还是存在刚才的问题。

虽然引入SessionID并没有彻底解决安全问题,但是这个方法是相对安全的。因为互联网上是不存在绝对的安全的,网络中的信息随时是可能被别人破解的。

但是如果破解成本远大于破解带来的收益,那么就不会有人去破解这个信息了,这个信息就可以说是安全的。

引入SessionID的好处

引入SessionID后,用户的用户名和密码都是由服务器来维护的,本地Cookie文件中存的只是SessionID。

虽然SessionID有可能被盗取,但是服务器也有一些策略来保证用户信息的安全性。

  1. 服务器可以通过IP地址来判断用户登录的地址范围,如果用户在短时间内登录地址发生了巨大变化,此时服务器就会知道这个用户出异常了,就会清楚服务器中对应的SessionID。

这会要求用户在登录时重新输入用户名和密码,因为用户名和密码只有本人和服务器知道。重新验证后,就会将另一个IP识别为非法用户,加入黑名单中。将合法用户和非法用户分为白名单和黑名单。

  1. 当用户进行一些高权限的操作时,会让用户再次输入密码,再次确认信息。

非法用户是不知道正确的密码的,就会让非法用户不能进行高权限的操作。比如修改密码时,需要输入旧密码。

  1. SessionID也是会过期的,假如某一个SessionID只有1小时的有效期。

即使你的SessionID被盗用了,也仅仅是在1小时内被盗用,而且和权限也有限,因此影响不会太大。

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

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

相关文章

前端html+css+js常用总结快速入门

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 学习前端全套所有技术性价比低下且容易忘记&#xff0c;先入门学会所有基础的语法&#xff08;cssjsheml&#xff09;&#xff…

LabVIEW太赫兹波扫描成像系统

LabVIEW太赫兹波扫描成像系统 随着科技的不断发展&#xff0c;太赫兹波成像技术因其非电离性、高穿透性和高分辨率等特点&#xff0c;在生物医学、材料质量无损检测以及公共安全等领域得到了广泛的应用。然而&#xff0c;在实际操作中&#xff0c;封闭性较高的信号采集软件限制…

使用ffmpeg将视频解码为帧时,图像质量很差

当使用ffmpeg库自带的ffmpeg.exe对对视频进行解帧或合并时&#xff0c;结果质量很差。导致这种原因的是在使用ffmpeg.exe指令进行解帧或合并时使用的是默认的视频码率&#xff1a;200kb/s。 如解帧指令&#xff1a; ffmpeg.exe -i 600600pixels.avi -r 2 -f image2 img/%03d.…

AI绘图:Stable Diffusion WEB UI 详细操作介绍:进阶-面部修复和调参

结合两篇文章完成了本地部署和基础操作,现在我们来介绍下进阶内容:面部修复,高清修复和调参区。 一:脸部修复 面部修复的适用在画真人、三次元的场景,特别是在画全身的时候 一般在画全身,由于脸部占比的空间比较小,那么绘制出来的效果就会比较差 1.面部修复 SD 支持…

日志服务 HarmonyOS NEXT 日志采集最佳实践

作者&#xff1a;高玉龙&#xff08;元泊&#xff09; 背景信息 随着数字化新时代的全面展开以及 5G 与物联网&#xff08;IoT&#xff09;技术的迅速普及&#xff0c;操作系统正面临前所未有的变革需求。在这个背景下&#xff0c;华为公司自主研发的鸿蒙操作系统&#xff08…

Linux Shell:`cat`命令

Linux Shell&#xff1a;cat命令 Linux 系统中的 cat 命令是一种多用途的工具&#xff0c;主要用于查看、创建、连接和追加文件内容。其名称来源于 concatenate 的缩写&#xff0c;意味着它可以用来连接文件内容到标准输出&#xff08;屏幕&#xff09;。在日常使用中&#xf…

算法基础--二分

&#x1f600;前言 二分查找是一种常见的算法技巧&#xff0c;通过不断缩小搜索范围&#xff0c;快速找到目标值的算法。在实际应用中&#xff0c;二分查找可以应用于有序数组中的查找、求上界、求下界等问题&#xff0c;具有较高的效率和广泛的应用价值。 &#x1f3e0;个人主…

scoped原理及使用

一、什么是scoped&#xff0c;为什么要用 在vue文件中的style标签上&#xff0c;有一个特殊的属性&#xff1a;scoped。 当一个style标签拥有scoped属性时&#xff0c;它的CSS样式就只能作用于当前的组件&#xff0c;通过该属性&#xff0c;可以使得组件之间的样式不互相污染。…

C# 登录界面代码

背景 MVVM 是一种软件架构模式&#xff0c;用于创建用户界面。它将用户界面&#xff08;View&#xff09;、业务逻辑&#xff08;ViewModel&#xff09;和数据模型&#xff08;Model&#xff09;分离开来&#xff0c;以提高代码的可维护性和可测试性。 MainWindow 类是 View&a…

网络协议——VRRP(虚拟路由冗余协议)原理与配置

1. VRRP概述 单网关出现故障后下联业务中断&#xff0c;配置两个及以上的网关时由于IP地址冲突&#xff0c;导致通讯时断时续甚至通信中断。VRRP组播类的网络层协议 2. 协议版本 VRRP v2: 支持认证,仅适用于IPv4网络 VRRP v3: 不支持认证&#xff0c; 适用于IPv4和IPv6两种网…

ES学习日记(九)-------logstash导入数据

一、安装和下载 es官网下载地址 官方介绍:Logstash是开源的服务器端数据处理管道&#xff0c;能够同时从多个来源采集数据&#xff0c;转换数据&#xff0c;然后将数据发送到您最喜欢的“存储库”中。(我们的存储库当然是 Elasticsearch。) 下载和ES一样的版本(很重要,必须这…

适配器: stack与queue

模板的使用 容器的复用 传容器: 控制底层是那个控制传仿函数: 控制大小堆的建立 stack 特点: 后进先出底层: 容器的封装(vector, list, dequeue)场景: 模拟递归, 函数压栈等接口:empty(), size(), top(), push(), pop()代码: stack queue 特点: 先进先出底层: 容器的封装…

Linux中的shell脚本之流程控制循环遍历

3 条件判断 4 流程控制语句 1&#xff09;if 语句 案例&#xff0c;用户输入用户名和密码&#xff0c;判断用户名是否是admin,密码是否是123,如果正确&#xff0c;则显示登录成功 首先我创建了shell文件&#xff0c;touch getpawer 其中getpawer 是我自己命的名 #!/bin/bas…

基于SpringBoot的药品管理系统设计与实现

介绍 药品管理员&#xff1a; 登录、退出、药品信息录入、药厂信息录入、采购人信息录入、药品信息浏览、药厂信息浏览、采购人信息浏览、药品信息查询入库修改删除、药信息查询修改删除采购人信息查询修改删除、入库记录浏览出库记录浏览、系统帮助 取药处人员: 登录、退出、…

[源码] Android 上的一些快捷方式,如通知、快捷方式等

目录 一、通知0. 配置权限1. 测试发送通知代码2. 打开通知设置界面代码3. 前台服务创建常驻通知 二、快捷方式1. 测试添加动态快捷方式代码 三、开发者图块四、桌面小部件 基于jetpack compose 框架的使用代码 一、通知 参见 官方文档 0. 配置权限 <uses-permission andr…

Matlab梁单元有限元编程:铁木辛柯梁VS欧拉梁

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

PTA题解 --- 天梯赛的赛场安排(C语言)

今天是PTA题库解法讲解的第八天&#xff0c;今天我们要讲解天梯赛的赛场安排&#xff0c;题目如下&#xff1a; 解题思路&#xff1a; 这个问题的关键在于高效地为参赛学校的队员分配赛场&#xff0c;同时满足给定的条件。我们可以通过以下步骤解决这个问题&#xff1a; 存储每…

[C#]OpenCvSharp使用HoughCircles霍夫圆检测算法找出圆位置并计数

【效果展示】 原图&#xff1a; 找出位置&#xff1a; 【测试环境】 vs2019,netframework4.7.2,opencvsharp4.8.0 【函数用法】 cv2提供了一种圆检测的方法&#xff1a;HoughCircles。该函数的返回结果与参数设置有很大的关系。 检测的图像时9枚钱币&#xff0c;分别使用了…

fiverr是什么?fiverr续费订阅充值教程?

一、什么是Fiverr Fiverr是国际上非常有名的自由职业在线平台&#xff0c;汇聚了大大小小200多个类别的超过80万的自由职业者&#xff0c;网站用户活跃度非常高&#xff0c;发展至今也已经成为一个全球性外包服务平台。Fiverr最初是由Micha Kaufman和Shai Wininger于2010年创立…

注意力机制篇 | YOLOv8改进之添加LSKAttention大核卷积注意力机制 | 即插即用,实现有效涨点

前言:Hello大家好,我是小哥谈。LSKAttention是一种注意力机制,它在自然语言处理领域中被广泛应用。LSKAttention是基于Transformer模型中的Self-Attention机制进行改进的一种变体。在传统的Self-Attention中,每个输入序列中的元素都会与其他元素进行交互,以获取全局的上下…