应用层协议 —— HTTP(二)

发布于:2022-12-03 ⋅ 阅读:(368) ⋅ 点赞:(0)

目录

一、认识URL

1. URL基本了解 

2. urlencode和urldecode

二、HTTP报文内的HTTP信息

1. HTTP报文

2. 请求报文和响应报文的结构

1. 构建http请求

2. 构建http响应

3. HTTP方法

1. GET 与 POST的区别

2. GET 和 POST的总结 

4. HTTP状态码

1. 2XX状态码 

2. 3XX状态码 

3. 4XX状态码 

4. 5XX状态码 

5. HTTP常见Header 

6. Cookie和Session 

三、HTTPS协议

1. HTTPS的认识

1. 对称加密和非对称加密


一、认识URL

1. URL基本了解 

        平时我们俗称的 "网址" 其实就是说的 URL(统一资源定位符,它是URI(统一资源标识符)的一个子集,是URI概念的一种实现方式);IP加端口可以定位互联网上唯一一台主机,但如果我们还需要该主机上的资源文件,还要有路径。即我们通过IP+路径,就可以唯一地确认一个网络资源。

        IP通常是以域名的方式呈现的,路径就是Linux主机上的目录,如下图所示:

协议方案名:

        http、https协议或其他
登录信息(认证):

        指定用户名和密码作为服务器端获取资源时的必要信息,此项为可选项,浏览器显示时会隐藏 
服务器地址:

        访问服务器时必须指明服务器地址,上图给出的只是方便人们记忆的网址,实际会由DNS(域名解析器)进行解析

服务器端口号:

        指定服务器连接的网络端口号,也是一个可选项,其中有些端口号非常有名,属于强绑定了,如果用户省略则会使用默认的端口号
带层次的文件路径:

        指定服务器的文件路径来定位指定的资源。和UNIX系统目录结构类似,但这不是根目录,而是一个部署好的web根目录。
(注意"?"号前面就是基本的URL格式,如果需要传入参数,在"?"号后面加入,以K-V形式传入)

查询字符串:

        百度搜索时就按照这种方式传参 (注意“?”°好前面就是基本的url格式,如果需要传入参数,在“?”号后面加入,以K-V形式传入)查询宁符串:百度搜索时就按照这种方式传参

2. urlencode和urldecode

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

转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY 格式

 "+" 被转义成了 "%2B"

 "?"被转义成了 "%3F"

        因此在编写服务器时,面对这些特殊字符,一定要做编码处理,下面是一个在线的编码/解码工具  

编码/转码工具

在输入C++后点击编码就能得到编码后的结果

        当服务器拿到对应的URL后,也需要对编码后的参数进行解码,此时服务器才能拿到你想要传递的参数,解码实际上就是编码的逆过程。(转义的过程称为urlencode,其逆过程称为urldecode 

二、HTTP报文内的HTTP信息

1. HTTP报文

        用于HTTP协议交互的信息被称为HTTP报文。请求端(客户端)的HTTP报文叫做请求报文,响应端(服务器端)的叫做响应报文。HTTP报文本身是有多行数据构成的字符串文本;

        HTTP报文大致可分为报文首部和报文主体两块。两者由空行来划分。通常,并不一定要有报文主体。

2. 请求报文和响应报文的结构

HTTP请求由以下四部分组成:

  • 请求行:[请求方法]+[url]+[http版本]
  • 请求报头:请求的属性,这些属性都是以key : value的形式按行陈列的。
  • 空行:遇到空行表示请求报头结束。
  • 请求正文:请求正文允许为空字符串,如果请求正文存在,则在请求报头中会有一个Content-Length属性来标识请求正文的长度。

如何将HTTP请求的报头与有效载荷进行分离?

        当应用层收到一个HTTP请求时,它必须想办法将HTTP的报头与有效载荷进行分离。对于HTTP请求来讲,这里的请求行和请求报头就是HTTP的报头信息,而这里的请求正文实际就是HTTP的有效载荷。

        我们可以根据HTTP请求当中的空行来进行分离,当服务器收到一个HTTP请求后,就可以按行进行读取,如果读取到空行则说明已经将报头读取完毕,实际HTTP请求当中的空行就是用来分离报头和有效载荷的。

        如果将HTTP请求想象成一个大的线性结构,此时每行的内容都是用\n隔开的,因此在读取过程中,如果连续读取到了两个\n,就说明已经将报头读取完毕了,后面剩下的就是有效载荷了。

-------------------------------------------------------------------------------------------------------------------------

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

  • 状态行:[http版本]+[状态码]+[状态码描述]
  • 响应报头:响应的属性,这些属性都是以key: value的形式按行陈列的。
  • 空行:遇到空行表示响应报头结束。
  • 响应正文:响应正文允许为空字符串,如果响应正文存在,则响应报头中会有一个Content-Length属性来标识响应正文的长度。比如服务器返回了一个html页面,那么这个html页面的内容就是在响应正文当中的。

如何将HTTP响应的报头与有效载荷进行分离?

        对于HTTP响应来讲,这里的状态行和响应报头就是HTTP的报头信息,而这里的响应正文实际就是HTTP的有效载荷。与HTTP请求相同,当应用层收到一个HTTP响应时,也是根据HTTP响应当中的空行来分离报头和有效载荷的。当客户端收到一个HTTP响应后,就可以按行进行读取,如果读取到空行则说明报头已经读取完毕。

1. 构建http请求

#include "sock.hpp"
#define SIZE 1024*10

void Usage(string proc)
{
     cout << "Usage: " << proc << " port" << endl;
}

void* HandlerHttpRequest(void* args)
{
     int sock = *(int*)args;
     delete (int*)args;
     pthread_detach(pthread_self());
     char buffer[SIZE];
     memset(buffer, 0, sizeof(buffer));
     ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
     if(s > 0)
     {
         buffer[s] = 0;
         cout << "get http request begin..." << endl;                                                                                                                                                                                                                                                                                                                                                                                                               
         cout << buffer << endl; //查看http的请求格式
         cout << "get http request end..." << endl;
     }
     close(sock);
     return nullptr;
 
}

int main(int argc, char* argv[])
{
     if(argc != 2)
     {
         Usage(argv[0]);
         exit(1);
     }
 
     uint16_t port = atoi(argv[1]);
     int listen_sock = Sock::Socket();
     Sock::Bind(listen_sock, port);
     Sock::Listen(listen_sock);
 
     for( ; ; )
     {
         int sock = Sock::Accept(listen_sock);
         if(sock > 0)
         {
             pthread_t tid;
             int* parm = new int(sock);
             pthread_create(&tid, nullptr, HandlerHttpRequest, parm);
         }
     }
     return 0;
}

我们通过浏览器访问时,只是访问了一次,为什么收到了多次HTTP请求?

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

2. 构建http响应

#include "sock.hpp"
#define SIZE 1024*10

void Usage(string proc)
{
     cout << "Usage: " << proc << " port" << endl;
}

void* HandlerHttpRequest(void* args)
{
     int sock = *(int*)args;
     delete (int*)args;
     pthread_detach(pthread_self());
     char buffer[SIZE];
     memset(buffer, 0, sizeof(buffer));
     ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
     if(s > 0)
     {
         buffer[s] = 0;
         cout << "get http request begin..." << endl;                                                                                                                                                                                                                                                                                                                                                                                                               
         cout << buffer << endl; //查看http的请求格式

         //构建http响应
         string http_response = "http/1.0 200 OK\n";//状态行
         http_response += "Content-Type: text/plain\n"; // text/plain,正文是普通文本
         http_response += "\n"; // 空行,用来区分报头和有效载荷
         http_response += "hello linux, hello world!!!";
         send(sock, http_response.c_str(), http_response.size(), 0);
         cout << "get http request end..." << endl;
     }
     close(sock);
     return nullptr;
 
}

int main(int argc, char* argv[])
{
     if(argc != 2)
     {
         Usage(argv[0]);
         exit(1);
     }
 
     uint16_t port = atoi(argv[1]);
     int listen_sock = Sock::Socket();
     Sock::Bind(listen_sock, port);
     Sock::Listen(listen_sock);
 
     for( ; ; )
     {
         int sock = Sock::Accept(listen_sock);
         if(sock > 0)
         {
             pthread_t tid;
             int* parm = new int(sock);
             pthread_create(&tid, nullptr, HandlerHttpRequest, parm);
         }
     }
     return 0;
}

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

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

1. GET 与 POST的区别

在比较这两种方法之前,我们先来看一下下面两个相应结果:

        url当中的 / 不能称之为我们云服务器上根目录,这个/ 表示的是web根目录;一般我们在请求资源时,都是具体的路劲;但是如果请求的  /  ,那么意味着我们请求的是网站首页。这个web根目录可以是你的机器上的任何一个目录,这个是可以自己指定的,不一定就是Linux的根目录。

接下来我们编写如下的代码,来对上面的描述做一些验证 

        首先,在当前目录下创建一个目录,作为我们的web根目录,然后在这个目录下,创建一个名为.html的文件,作为网站的首页

如下是网站首页的代码: 

<!DOCTYPE html>
 
<html>
     <head>
         <meta charset="utf-8">                                                                                                                                                    
     </head>
     <body>
         <h3>hello linux!</h3>
     </body>
</html>

 如下是http.cc


#include "sock.hpp"
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fstream>
#define WWWROOT "./wwwroot/"
#define HOME_PAGE "index.html"
#define SIZE 1024*10
 
void Usage(string proc)
{
     cout << "Usage: " << proc << " port" << endl;
}
 
void* HandlerHttpRequest(void* args)
{
     int sock = *(int*)args;
     delete (int*)args;
     pthread_detach(pthread_self());
     char buffer[SIZE];
     memset(buffer, 0, sizeof(buffer));
     ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
     if(s > 0)
     {
         buffer[s] = 0;
         cout << buffer << endl; //查看http的请求格式
         string html_file = WWWROOT;
         html_file += HOME_PAGE;//html_file就是指定的路径
         struct stat st;
         stat(html_file.c_str(), &st);//该函数可以通过指定的路径获取特定的属性,其目的是为了获取到Content-Length的大小

         //返回的时候,不仅仅是返回正文网页信息,而是还要包括http的请求
         string http_response = "http/1.0 200 OK\n";
         //正文部分的数据类型
         http_response += "Content-Type: text/html; charset=utf8\n";
         http_response += "Content-Length: ";
         http_response += to_string(st.st_size);
         http_response += "\n";
         http_response += "\n";

         //正文
         ifstream in(html_file);
         if(!in.is_open())
         {
             cerr << "open html error!" << endl;
         }
         else
         {
             string content;
             string line;
             while(getline(in, line))
             {
                 content += line;
             }
             http_response += content;
             in.close();
             cout << http_response << endl;
             send(sock, http_response.c_str(), http_response.size(), 0);
         }
     }
     close(sock);
     return nullptr;
 
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
 
int main(int argc, char* argv[])
{
     if(argc != 2)
     {
         Usage(argv[0]);
         exit(1);
     }
 
     uint16_t port = atoi(argv[1]);
     int listen_sock = Sock::Socket();
     Sock::Bind(listen_sock, port);
     Sock::Listen(listen_sock);
 
     for( ; ; )
     {
         int sock = Sock::Accept(listen_sock);
         if(sock > 0)
         {
             pthread_t tid;
             int* parm = new int(sock);
             pthread_create(&tid, nullptr, HandlerHttpRequest, parm);
         }
     }
     return 0;
}

演示结果:其中wwwroot就叫做web根目录,wwwroot目录下的访问的内容,都叫做资源

 接下来我们通过表单的方式来演示GET和POST的区别

<!DOCTYPE html>

<html>
     <head>
         <meta charset="utf-8"> 
     </head>
     <body>
         <h5>hello 我是首页!</h5>
         <h5>hello 我是表单!</h5>
         <form action="/a/b/handler_form" method="GET">                                                                                                                            
             姓名:<input type="text" name="name"><br/>
             密码:<input type="password" name="passwd"><br/>
             <input type="submit" value="登陆">
         </form>
 
     </body>
</html>

接下来让我们来访问一下服务器,下图是GET访问的结果:

        当我们访问服务器后,输入姓名和密码后点击登录,会发现我们的姓名和密码在URL当中。所以GET方法,如果提交参数,是通过URL的方式进行提交的。这样前端的数据将会被后端的C++代码拿到,如果是一套完整的服务,那么就会登录成功。

<!DOCTYPE html>

<html>
     <head>
         <meta charset="utf-8"> 
     </head>
     <body>
         <h5>hello 我是首页!</h5>
         <h5>hello 我是表单!</h5>
         <form action="/a/b/handler_form" method="POST">                                                                                                                            
             姓名:<input type="text" name="name"><br/>
             密码:<input type="password" name="passwd"><br/>
             <input type="submit" value="登陆">
         </form>
 
     </body>
</html>

下图是POST访问的结果:

 我们可以看到在URL当中并没有我们提交的参数,而是在我们的正文当中

2. GET 和 POST的总结 

结论一:概念问题

GET方法叫做:获取,是最常用的方法;

        默认一般获取所有的网页,都是GET方法,但是如果GET要提交参数(它能的!!),通过URL来进行参数拼接从而提交给server端 
POST方法叫做:推送,是提交参数比较常用的方法;

        但是如果提交参数,一般是通过正文部分提交的。但是你不要忘记,Content-Length: XXX表示参数的长度;

        当我们想获取某种资源的时候,大部分都是GET,但如果想提交参数既可以是GET也可以是POST。
结论二:区别

参数提交的位置不同,
1.POST方法比较私密(私密!=安全),不会回显到浏览器的url输入框!。

   GET方法不私密,会将重要信息回显到url的输入框中,增加了被盗取的风险 
2. GET是通过url传参的,而url是有大小限制的!和具体的浏览器有关! 
    POST方法,是由正文部分传参的,一般大小没有限制! 发布方法,是由正文部分传参的,一般大小没有限制!

结论三:如何选择
1. GET:如果提交的参数,不敏感,数量非常少,可以采用GET2。

2. POST:否则,就使用POST方法

其实GET和POST是前后端交互的一种方式

4. HTTP状态码

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

最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

1. 2XX状态码 

2XX的响应结果表明请求被正常处理了

① 200 OK 

表示从客户端发来的请求在服务器被正常处理了。

在响应报文内,随状态码一起返回的信息会因方法的不同而发生改变。

比如:使用GET方法时,对应请求资源的实体回作为响应返回;而使用HEAD方法时,对应请求资源的实体不随报文首部作为响应返回(即在响应中只返回首部,不会返回实体的主体部分) 

② 204 No Content 

该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。另外,也不允许返回任何实体的主体。一般在需要从客户端往服务器发送消息,而对客户端不需要发送新消息内容的情况下使用。

③ 206 Partial Content 

该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。响应报文中包含由Content-Range指定范围的实体内容。 

2. 3XX状态码 

① 301 Moved Permanently

永久性重定向。该状态码表示请求的资源已被分配了新的URL,以后应使用资源现在所指的URL。也就是说,如果已经把资源对应的URL保存为书签了,这时应该按Location首部字段提示的URL重新保存。

② 302 Found 和 307 Temporary Redirect

临时重定向。该状态码表示请求的资源已被分配了新的URL,希望用户 (本次)能使用新的URL访问。比如:我们每次美团下单后都会转入支付界面,付款结束后,会提示3秒后跳转到下单界面(初始界面)。我们把这种称为临时重定向。

3. 4XX状态码 

4XX的响应结果表明客户端是发生错误的原因所在

① 400 Bad Request 

该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发生请求。另外,浏览器会像200 OK 一样对待该状态码。(即给出提示)

② 403 Forbidden

该状态码表明对请求资源的访问被服务器拒绝了。服务器端没有必要给出拒绝的详细理由,但如果想作说明得到话,可以在实体的主体部分对原因进行描述,这样就能让用户看到了。

③ 404 Not Found

该状态码表明服务器上无法找到请求的资源。

4. 5XX状态码 

5XX的响应结果表明服务器本身发生了错误 

① 500 Internal Server Error

该状态码表明服务器端执行请求时发生了错误。也有肯能是Web应用存在的bug或某种临时的故障。

② 503 Service Unavailable

该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法请求。

5. HTTP常见Header 

  • Content-Type:数据类型(text/html等)。
  • Content-Length:正文的长度。
  • Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
  • User-Agent:声明用户的操作系统和浏览器的版本信息。
  • Referer:当前页面是哪个页面跳转过来的。
  • Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。
  • Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能。

Host

        首部字段Host会告知服务器,请求的资源所处的互联网主机名和端口号。Host首部字段在HTTP/1.1规范内是唯一一个必须被包含在请求内的首部字段。
        首部字段Host和以单台服务器分配多个域名的虚拟主机的工作机制有很密切的关联,这是首部字段Host必须存在的意义。
        请求被发送至服务器时,请求中的主机名会用IP地址直接替换解决。但如果这时,相同的IP地址下部署运行着多个域名,那么服务器就会无法理解究竟是哪个域名对应的请求。因此,就需要使用首部字段Host来明确指出请求的主机名。若服务器未设定主机名,那直接发送一个空值即可。

User-Agent

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

比如当我们用电脑下载某些软件时,它会自动向我们展示与我们操作系统相匹配的版本,这实际就是因为我们在向目标网站发起请求的时候,User-Agent字段当中包含了我们的主机信息,此时该网站就会向你推送相匹配的软件版本。

Referer                                                                                                                                              首部字段Referer会告知服务器请求的原始资源的URL。客户端一般都会发送Referer首部字段给服务器。但当直接在浏览器的地址栏输人URL,或出于安全性的考虑时,也可以不发送该首部字段。因为原始资源的URL中的查询字符串可能含有ID和密码等保密信息,要是写进Referer转发给其他服务器,则有可能导致保密信息的泄露。     

6. Cookie和Session 

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

        比如当你登录一次CSDN后,就算你把CSDN网站关了甚至是重启电脑,当你再次打开CSDN网站时,CSDN并没有要求你再次输入账号和密码,这实际上是通过cookie技术实现的,点击浏览器当中锁的标志就可以看到对应网站的各种cookie数据。

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

cookie是什么呢?   

        因为HTTP是一种无状态协议,如果没有cookie的存在,那么每当我们要进行页面请求时都需要重新输入账号和密码进行认证,这样太麻烦了。

        比如你是某个视频网站的VIP,这个网站里面的VIP视频有成百上千个,你每次点击一个视频都要重新进行VIP身份认证。而HTTP不支持记录用户状态,那么我们就需要有一种独立技术来帮我们支持,这种技术目前现在已经内置到HTTP协议当中了,叫做cookie。

        当我们第一次登录某个网站时,需要输入我们的账号和密码进行身份认证,此时如果服务器经过数据比对后判定你是一个合法的用户,那么为了让你后续在进行某些网页请求时不用重新输入账号和密码,此时服务器就会进行Set-Cookie的设置。(Set-Cookie也是HTTP报头当中的一种属性信息)

        当认证通过并在服务端进行Set-Cookie设置后,服务器在对浏览器进行HTTP响应时就会将这个Set-Cookie响应给浏览器。而浏览器收到响应后会自动提取出Set-Cookie的值,将其保存在浏览器的cookie文件当中,此时就相当于我的账号和密码信息保存在本地浏览器的cookie文件当中。

        从第一次登录认证之后,浏览器再向该网站发起的HTTP请求当中就会自动包含一个cookie字段,其中携带的就是我第一次的认证信息,此后对端服务器需要对你进行认证时就会直接提取出HTTP请求当中的cookie字段,而不会重新让你输入账号和密码了。

也就是在第一次认证登录后,后续所有的认证都变成了自动认证,这就叫做cookie技术。

cookie被盗

        如果你浏览器当中保存的cookie信息被非法用户盗取了,那么此时这个非法用户就可以用你的cookie信息,以你的身份去访问你曾经访问过的网站,我们将这种现象称为cookie被盗取了。

        比如你不小心点了某个链接,这个链接可能就是一个下载程序,当你点击之后它就会通过某种方式把程序下载到你本地,并且自动执行该程序,该程序会扫描你的浏览器当中的cookie目录,把所有的cookie信息通过网络的方式传送给恶意方,当恶意方拿到你的cookie信息后就可以拷贝到它的浏览器对应的cookie目录当中,然后以你的身份访问你曾经访问过的网站。

SessionID

        单纯的使用cookie是非常不安全的,因为此时cookie文件当中就保存的是你的私密信息,一旦cookie文件泄漏你的隐私信息也就泄漏。

        所以当前主流的服务器还引入了SessionID这样的概念,当我们第一次登录某个网站输入账号和密码后,服务器认证成功后还会服务端生成一个对应的SessionID,这个SessionID与用户信息是不相关的。系统会将所有登录用户的SessionID值统一维护起来。

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

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

安全是相对的

        引入SessionID之后,浏览器当中的cookie文件保存的是SessionID,此时这个cookie文件同样可能被盗取。此时用户的账号和密码虽然不会泄漏了,但用户对应的SessionID是会泄漏的,非法用户仍然可以盗取我的SessionID去访问我曾经访问过的服务器,相当于还是存在刚才的问题。

  • 之前的工作方式就相当于把账号和密码信息在浏览器当中再保存一份,每次请求时都自动将账号和密码的信息携带上,但是账号和密码一直在网当中发送太不安全了。
  • 因此现在的工作方式是,服务器只有在第一次认证的时候需要在网络中传输账号和密码,此后在网络上发送的都是SessionID。

        这种方法虽然没有真正解决安全问题,但这种方法是相对安全的。互联网上是不存在绝对安全这样的概念的,任何安全都是相对的,就算你将发送到网络当中的信息进行加密,也有可能被别人破解。 不过在安全领域有一个准则:如果破解某个信息的成本已经远远大于破解之后获得的收益(说明做这个事是赔本的),那么就可以说这个信息是安全的。

引入SessionID后的好处 

  • 在引入SessionID之前,用户登录的账号信息都是保存在浏览器内部的,此时的账号信息是由客户端去维护的。
  • 而引入SessionID后,用户登录的账号信息是有服务器去维护的,在浏览器内部保存的只是SessionID。
  • 此时虽然SessionID可能被非法用户盗取,但服务器也可以使用各种各样的策略来保证用户账号的安全。
  • IP是有归类的,可以通过IP地址来判断登录用户所在的地址范围。如果一个账号在短时间内登录地址发送了巨大变化,此时服务器就会立马识别到这个账号发生异常了,进而在服务器当中清除对应的SessionID的值。这时当你或那个非法用户想要访问服务器时,就都需要重新输入账号和密码进行身份认证,而只有你是知道自己的密码的,当你重新认证登录后服务器就可以将另一方识别为非法用户,进而对该非法用户进行对应的黑名单/白名单认证。
  • 当操作者想要进行某些高权限的操作时,会要求操作者再次输入账号和密码信息,再次确认身份。就算你的账号被非法用户盗取了,但非法用户在改你密码时需要输入旧密码,这是非法用户在短时间内无法做到的,因为它并不知道你的密码。这也就是为什么账号被盗后还可以找回来的原因,因为非法用户无法在短时间内修改你的账号密码,此时你就可以通过追回的方式让当前的SessionID失效,让使用该账号的用户进行重新登录认证。
  • SessionID也有过期策略,比如SessionID是一个小时内是有效的。所以即便你的SessionID被非法用户盗取了,也仅仅是在一个小时内有效,而且在功能上受约束,所以不会造成太大的影响。
  • 任何事情都有两面性,如果不是这些非法用户的存在,现在的服务器肯定是漏洞百出,只有双方不断进行对抗双方才能不断进步。

此处原文链接:https://blog.csdn.net/chenlong_cxy/article/details/124787316

三、HTTPS协议

1. HTTPS的认识

        早期很多公司刚起步的时候,使用的应用层协议都是HTTP,而HTTP无论是用GET方法还是POST方法传参,都是没有经过任何加密的,因此早期很多的信息都是可以通过抓包工具抓到的。

        为了解决这个问题,于是出现了HTTPS协议,HTTPS实际就是在应用层和传输层协议之间加了一层加密层(SSL&TLS),这层加密层本身也是属于应用层的,它会对用户的个人信息进行各种程度的加密。HTTPS在交付数据时先把数据交给加密层,由加密层对数据加密后再交给传输层。

1. 对称加密和非对称加密

加密的方式可以分为对称加密和非对称加密:

  • 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密。
  • 采用公钥和私钥来进行加密和解密,用其中一个密钥进行加密就必须用另一个密钥进行解密,这种加密方法称为非对称加密。