# TCP/IP 协议栈
📌 1. OSI 七层模型(ISO 协议),从上到下分别如下。
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层
📌 2. TCP/IP 四层模型,从上到下分别如下。
应用层
传输层
网络层
网络接口层
也有的是把 TCP/IP 分成了五层模型,其实是差不多的。
应用层
传输层
网络层
数据链路层
物理层
📌 3. TCP/IP 四层模型每层的作用。
应用层
为用户提供所需要的各种服务,例如:HTTP、FTP、DNS、SMTP、SSH、TELNET、POP3 等。
传输层
为应用层实体提供端到端的通信功能,保证数据包的顺序传送及数据的完整性。
该层定义了两个主要的协议:传输控制协议(TCP)和用户数据报协议(UDP)。
TCP 是面向连接的协议,UDP 是无连接的协议。
网络层
主要解决主机到主机的通信问题。IP 协议是网际互联层最重要的协议。
当我们去 ping 一个网址的时候,用到的协议是 ICMP 协议,这个协议也在网络层中。
该层也叫做 IP 层。
网络接口层
负责监视数据在主机和网络之间的交换。
发送数据的时候是一层一层往下传的,接收数据的时候是一层一层往上传的。
📌 4. HTTP 和 HTTPS 在 TCP/IP 协议栈中的位置
目前普遍应用的版本是 HTTP/1.1,正在逐步向 HTTP/2.0 迁移。
HTTP 的默认端口是
80
,HTTPS 的默认端口是443
。HTTP/1.1 和 HTTP/1.0 之间的区别就是,新增了 Keep-Alive(长连接)特性。
# TCP 协议数据传输过程
TCP 这一层对数据的封装如下。TCP 传输数据时,是把一个大数据包切成若干个小数据包进行发送的,这些小数据包根据 Sequence Number 进行排序,等到了接收方那里再根据各自的 Sequence Number 重新组装起来,这样的话能提高传输效率。
# TCP 协议模型详解
OSI 中的层 | 功能 | TCP/IP 协议族 |
---|---|---|
应用层 | 文件传输,电子邮件,文件服务,虚拟终端 | TFTP,HTTP,SNMP,FTP,SMTP,DNS,TELNET |
表示层 | 数据格式化,代码转换,数据加密 | 没有协议 |
会话层 | 解除或建立与别的节点的联系 | 没有协议 |
传输层 | 提供端对端的接口 | TCP,UDP |
网络层 | 为数据包选择路由 | IP,ICMP,RIP,OSPF,BGP,IGMP |
数据链路层 | 传输有地址的帧以及错误检测功能 | SLIP,CSLIP,PPP,ARP,RARP,MTU |
物理层 | 以二进制数据形式在物理媒体上传输数据 | ISO2110,IEEE802,IEEE802.2 |
# TCP 三次握手和四次挥手
补充
正常状态下,数据传输完成后,都是由客户端来断开连接;非正常状态下,如果客户端一直没有传输数据,服务器端也会自己断开连接。
当客户端发送 FIN 请求断开连接时,此时客户端就没法再发送有效数据了,但是可以继续发送请求命令。
图中像 SYN_SENT 等这些状态,在 mac 或者 linux 上可以通过
netstat -an
命令查看。TCP 协议端口状态说明。
- LISTENING
提供某种服务,侦听远方 TCP 端口的连接请求,当提供的服务没有被连接时,处于 LISTENING 状态,端口是开放的,等待被连接。
- SYN_SENT (客户端状态)
客户端调用 connect,发送一个 SYN 请求建立一个连接,在发送连接请求后等待匹配的连接请求,此时状态为 SYN_SENT。
- SYN_RCVD (服务端状态)
在收到和发送一个连接请求后,等待对方对连接请求的确认,当服务器收到客户端发送的同步信号时,将标志位 ACK 和 SYN 置1发送给客户端,此时服务器端处于 SYN_RCVD 状态,如果连接成功了就变为 ESTABLISHED,正常情况下 SYN_RCVD 状态非常短暂。
- ESTABLISHED
ESTABLISHED 状态是表示两台机器正在传输数据。
- FIN_WAIT_1
等待远程 TCP 连接中断请求,或先前的连接中断请求的确认,主动关闭端应用程序调用 close,TCP 发出 FIN 请求主动关闭连接,之后进入 FIN_WAIT_1 状态。
- FIN_WAIT_2
从远程 TCP 等待连接中断请求,主动关闭端接到 ACK 后,就进入了 FIN_WAIT_2。这是在关闭连接时,客户端和服务器两次握手之后的状态,是著名的半关闭的状态了,在这个状态下,应用程序还有接受数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于 FIN_WAIT_2 状态,而服务器则一直处于 WAIT_CLOSE 状态,而直到应用层来决定关闭这个状态。
- CLOSE_WAIT
等待从本地用户发来的连接中断请求,被动关闭端 TCP 接到 FIN 后,就发出 ACK 以回应 FIN 请求(它的接收也作为文件结束符传递给上层应用程序),并进入CLOSE_WAIT。
- CLOSING
等待远程 TCP 对连接中断的确认,处于此种状态比较少见。
- LAST_ACK
等待原来的发向远程 TCP 的连接中断请求的确认,被动关闭端一段时间后,接收到文件结束符的应用程序将调用 close 关闭连接,TCP 也发送一个 FIN,等待对方的 ACK,进入 LAST_ACK。
- TIME_WAIT
在主动关闭端接收到 FIN 后,TCP 就发送 ACK 包,并进入 TIME_WAIT 状态,等待足够的时间以确保远程 TCP 接收到连接中断请求的确认,很大程度上保证了双方都可以正常结束,但是也存在问题,须等待 2MSL 时间的过去才能进行下一次连接。
- CLOSED
被动关闭端在接受到 ACK 包后,就进入了 CLOSED 的状态,连接结束,没有任何连接状态。
# TCP 头包含的内容
源端⼝号和⽬标端⼝号:⽤于识别发送和接收的应⽤程序。
序列号:标识从 TCP 源到⽬标的字节流的顺序。
确认号:发送⽅期望收到的下⼀个字节的序列号。
数据偏移(头部⻓度):指示 TCP 头的⼤⼩。
标志位:如 SYN(建⽴连接)、ACK(确认)、FIN(结束连接)等。
窗⼝⼤⼩:⽤于流量控制,指示接收⽅还能接收的字节数。
校验和:⽤于错误检测。
紧急指针:仅在紧急标志被设置时有效。
# TCP 确保数据正确性的⽅式
数据包确认:接收⽅会对接收到的数据包发送确认。如果发送⽅没有收到确认,它会重传数据包。
顺序控制:TCP 数据包包含序列号,确保数据的顺序性。
校验和:每个 TCP 数据包都包含校验和,⽤于检测数据在传输过程中是否出现错误。
重传机制:丢失的数据包会被重新发送。
流量控制:通过窗⼝⼤⼩调整,控制数据的发送速率,防⽌接收⽅被快速发送的数据淹没。
拥塞控制:当⽹络拥堵时,TCP 会减少数据的发送量。
# TCP 和 UDP 的区别
TCP(传输控制协议)和 UDP(⽤户数据报协议)是两种最常⽤的传输层协议,它们在传输数据的⽅式和特性上有显著的区别。
1. 连接⽅式
TCP:是⼀种⾯向连接的协议。在数据传输之前,它需要在发送⽅和接收⽅之间建⽴连接。
UDP:是⼀种⽆连接协议。它发送数据时不需要建⽴连接,直接发送。
2. 可靠性
TCP:提供⾼可靠性的数据传输。它通过确认响应和重传丢失的数据包来确保数据正确性和完整性。
UDP:不保证数据包的顺序、完整性或正确性。它不会对丢失的数据包进⾏重传。
3. 顺序和完整性
TCP:确保数据包按照正确的顺序到达接收⽅。
UDP:不能保证数据包的顺序。
4. 速度和效率
TCP:由于建⽴连接、确认响应等过程,⽐ UDP 慢。
UDP:由于缺乏确认机制,通常⽐ TCP 快,适⽤于对实时性要求⾼的应⽤,如视频流和在线游戏。
5. 流量控制和拥塞控制
TCP:提供流量控制和拥塞控制机制。
UDP:没有这些机制。
# 传输层和⽹络层分别负责什么,端⼝在什么层标记
⽹络层主要负责数据包在⽹络中的传输和路由选择,⽽传输层则关注在这些数据包到达时如何保证它们之间的正确序列、完整性和应⽤间的正确分发。端⼝号的概念属于传输层,它⽤于区分主机上的不同应⽤程序或服务。
# 传输层的职责
1. 端到端的通信
传输层负责在⽹络的源点和终点之间提供端到端的数据传输。
2. 可靠性保证
对于 TCP(传输控制协议),传输层提供可靠的、顺序的和⽆差错的数据传输。这包括错误检测、数据重传、流量控制和拥塞控制。
3. 不同应⽤之间的通信
传输层通过端⼝号区分不同的⽹络应⽤。每个应⽤程序都可以绑定到特定的端⼝号,以便正确地接收和发送数据。
4. 协议
主要协议包括 TCP 和 UDP(⽤户数据报协议)。UDP 提供了⼀种⽐ TCP 更快但不可靠的传输⽅式,常⽤于视频流、在线游戏等对实时性要求⾼的应⽤。
# 网络层的职责
1. 路由和转发
⽹络层负责在复杂的⽹络中确定数据包的路径。路由器在这⼀层上⼯作,决定如何从源头到⽬的地⾼效地传输数据。
2. IP 地址
⽹络层使⽤ IP 地址来标识发送和接收数据的设备。IP 地址是⽹络层的主要部分,它确定了设备在⽹络中的位置。
3. 分包和组装
⽹络层能够将较⼤的数据分成较⼩的包,以便在⽹络中传输。在接收端,这些包被重新组装成原始数据。
4. 协议
主要协议是 IP(互联⽹协议)。在现代⽹络中,主要使⽤ IPv4 或 IPv6。
# 端⼝标记
端⼝号是传输层的⼀个概念,主要⽤于 TCP 和 UDP 协议。端⼝号帮助计算机区分不同的应⽤程序或服务,确保数据包能被正确地传送到相应的程序。
# 什么是 HTTP 协议
HTTP 是超文本传输协议,从 www 浏览器传输到本地浏览器的一种传输协议,网站是基于 HTTP 协议的,例如网站的图片、CSS、JS 等都是基于 HTTP 协议进行传输的。
HTTP 协议是对由从客户端到服务器的请求(Request)和从服务器到客户端的响应(response)进行约束和规范。
HTTP/1.0 的出现意味着 HTTP 协议成熟了,HTTP/1.1 的出现意味着 HTTP/1 协议已经很完善了。
在 HTTP/2 之后还有 HTTP/3,HTTP/3 相对于前两者来说是一种全新的协议,它把底层的协议给换了。前两者底层的协议是 TCP,而 HTTP/3 把底层协议换成了 UDP 协议。
- HTTP 协议的官方文档非常的晦涩难懂,而且枯燥。如果想学习协议的话,可以去看 中文 RFC 文档 (opens new window),HTTP 协议 (opens new window)。
# HTTP 的工作过程
一次 HTTP 操作(一个请求和一个响应)称为一个事务,其工作过程可分为四步:
首先客户机与服务器需要建立 TCP 连接。只要单击某个超级链接,HTTP 的工作开始。
建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是 MIME 信息,包括请求修饰符、客户机信息和可能的内容。
服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是 MIME 信息包括服务器信息、实体信息和可能的内容。
客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开 TCP 连接。
如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,由显示屏输出。对于用户来说,这些过程是由 HTTP 自己完成的,用户只要用鼠标点击,等待信息显示就可以了。
事务
所谓事务,就是有一个任务或操作,我们无法一次性完成,它包含若干个步骤,这些步骤相互之间有依赖性,当然有些步骤之间也有可能没有依赖性,像这样的任务或操作就叫做事务。
事务的特点就是,如果其中某个步骤失败了,那么依赖它的其他步骤也就无法执行,那么就认为这整个事务就失败了。而且事务可以回滚。
数据库里就有事务,但是事务并不是数据库专有的,在很多地方都有用到事务。
HTTP 工作过程的回滚不是我们常见的那种可以看到的回滚,比如关闭连接,释放内存等。
# HTTP 的请求与响应
HTTP 请求组成:请求行、消息报头、请求正文。
HTTP 响应组成:状态行、消息报头、响应正文。
请求行组成:以一个方法符号开头,后面跟着请求的 URL 和协议的版本。
状态行组成:服务器 HTTP 协议的版本,服务器发回的响应状态代码和状态代码的文本描述。
- HTTP 请求报文
- HTTP 请求报文例子
- HTTP 响应报文
- HTTP 响应报文例子
5. 常用的请求报头
Accept
请求报头域用于指定客户端接受哪些类型的信息。
注意,当我们在请求 JSON 接口时(JSON 接口是符合 RESTful 规范的),要指定 Accept:application/json,不然后端服务器那边无法正确返回数据。
Accept-Charset
请求报头域用于指定客户端接受的字符集。Accept-Encoding
请求报头域类似于 Accept,但是它是用于指定可接受的内容编码。在服务端设定。Accept-Language
请求报头域类似于 Accept,但是它是用于指定一种自然语言。在服务器设定。Authorization
请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization 请求报头域的请求,要求服务器对其进行验证。Host
请求报头域主要用于指定被请求资源的 Internet 主机和端口号,它通常从 HTTP URL 中提取出来的,发送请求时,该报头域是必需的。User-Agent
请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。Connection
指定连接类型。keep-alive 是长连接。Referer
用来表明当前页面的上一页是哪个页面。主要用来防盗链。
6. 常用的响应报头
Content-Type
指定响应体的格式以及字符集编码是什么。Set-Cookie
设置 cookie。
客户端向服务器上传 cookie 是整块上传的,但是服务器向浏览器设置 cookie 是逐条设置的。一个完整的 cookie 应该是有5个字段的。
Location
响应报头域用于重定向接受者到一个新的位置。Location 响应报头域常用在更换域名的时候。Server
响应报头域包含了服务器用来处理请求的软件信息。与 User-Agent 请求报头域是相对应的。WWW-Authenticate
响应报头域必须被包含在401(未授权的)响应消息中,客户端收到 401 响应消息时候,并发送 Authorization 报头域请求服务器对其进行验证时,服务端响应报头就包含该报头域。
7. 实体报头
请求和响应消息都可以传送一个实体。一个实体由实体报头域和实体正文组成,但并不是说实体报头域和实体正文要在一起发送,可以只发送实体报头域。实体报头定义了关于实体正文(eg:有无实体正文)和请求所标识的资源的元信息。
Content-Encoding
实体报头域被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得 Content-Type 报头域中所引用的媒体类型,必须采用相应的解码机制。
Content-Encoding: gzip 标记着传输内容会被压缩,但是报头并不会被压缩。
Content-Language
实体报头域描述了资源所用的自然语言。Content-Length
实体报头域用于指明实体正文的长度,以字节方式存储的十进制数字来表示。Content-Type
实体报头域用于指明发送给接收者的实体正文的媒体类型。Last-Modified
实体报头域用于指示资源的最后修改日期和时间。Expires
实体报头域给出响应过期的日期和时间。
# HTTP 的请求方法
HTTP/1.0 定义了三种请求方法:GET、POST 和 HEAD 方法。
HTTP/1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
具体可见:HTTP 请求方法 (opens new window)
HTTP 常见的8种请求方法如下:
GET:请求获取 Request-URI 所标识的资源。
POST:在 Request-URI 所标识的资源后附加新的数据。
HEAD:请求获取由 Request-URI 所标识的资源的响应消息报头。
PUT:请求服务器存储一个资源,并用 Request-URI 作为其标识。
DELETE:请求服务器删除 Request-URI 所标识的资源。
TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断。
CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项和需求。
以上的方法其实暗含了类似数据库的 CRUD 操作,CRUD 分别对应 PUT、GET、POST、DELETE。
# HTTP 状态码
状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx:指示信息--表示请求已接收,继续处理。
2xx:成功--表示请求已被成功接收、理解、接受。
3xx:重定向--要完成请求必须进行更进一步的操作。
4xx:客户端错误--请求有语法错误或请求无法实现。
5xx:服务器端错误--服务器未能实现合法的请求。
推荐图书
学会提问系列图书
# HTTP 的缓存机制
# 强制缓存
不需要发送请求到服务端,直接读取浏览器本地缓存,在 Chrome 的 Network 中显示的 HTTP 状态码是 200。
在 Chrome 中,强制缓存又分为 Disk Cache(存放在硬盘中)和 Memory Cache(存放在内存中),存放的位置是由浏览器控制的。
是否是强制缓存由 Cache-Control (opens new window)、Expires (opens new window) 和 Pragma (opens new window) 3 个 Header 属性共同控制。
# Cache-Control
Cache-Control (opens new window) 是 HTTP/1.1 中新增的属性,在请求头和响应头中都可以使用。
常用的指令有:
max-age:单位是秒,缓存时间计算的方式是距离发起的时间的秒数,超过间隔的秒数缓存失效。
s-max-age:覆盖 max-age 或者 Expires 头,但是仅适用于共享缓存,私有缓存会忽略它。
no-cache:不使用强制缓存,需要与服务器进行验证。
no-store:禁止使用缓存(包括协商缓存),每次都向服务器请求最新的资源。
private:专用于个人的缓存,中间代理、CDN 等不能缓存该响应。
public:共享缓存,表明该响应可以被中间代理、CDN 等缓存。
must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证。
# Expires
Expires (opens new window) 的值是一个 HTTP 日期,在浏览器发起请求时,会根据系统时间和 Expires 的值进行比较,如果系统时间超过了 Expires 的值,缓存失效。由于是和系统时间进行比较,所以当系统时间和服务器时间不一致的时候,会有缓存有效期不准的问题。
如果在 Cache-Control 响应头设置了 "max-age" 或者 "s-max-age" 指令,那么 Expires 头会被忽略。
# Pragma
Pragma (opens new window) 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用强制缓存,需要与服务器进行验证,在 3 个头部属性中的优先级最高。
它主要是用来向后兼容只支持 HTTP/1.0 协议的缓存服务器,那时候 HTTP/1.1 协议中的 Cache-Control 还没有出来。因此建议只在需要兼容 HTTP/1.0 客户端的场合下应用 Pragma。
# 协商缓存
当浏览器的强制缓存失效或者请求头中设置了不走强制缓存,并且在请求头中设置了 If-Modified-Since (opens new window) 或者 If-None-Match (opens new window) 的时候,会将这两个属性值带到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified (opens new window) 或者 ETag (opens new window) 属性。
# Etag/If-None-Match
Etag:web 服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。
If-None-Match:当资源过期时(使用 Cache-Control 标识的 max-age),发现资源具有 Etag 声明,则再次向 web 服务器请求时带上头 If-None-Match(Etag 的值)。web 服务器收到请求后发现有请求头 If-None-Match,则与被请求资源的相应校验串进行比对,决定返回 200 或 304。
# Last-Modified/If-Modified-Since
Last-Modified:表示这个响应资源的最后修改时间。web 服务器在响应请求时,告诉浏览器资源的最后修改时间。
If-Modified-Since:当资源过期时(使用 Cache-Control 标识的 max-age),发现资源具有 Last-Modified 声明,则再次向 web 服务器请求时带上头 If-Modified-Since,表示请求时间。web 服务器收到请求后发现有请求头 If-Modified-Since,则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源有被改动过,则响应整片资源内容(写在响应消息包体内)HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应 HTTP 304(无需包体,节省浏览),告知浏览器继续使用所保存的 cache。
Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag ,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。
# ETag 可以解决 Last-Modified 存在的一些问题
如果文件的修改频率在秒级以下,Last-Modified/If-Modified-Since 会错误地返回 304。
如果文件的修改时间变了,但是内容没有任何变化的时候,Last-Modified/If-Modified-Since 会错误地返回 200。
# Cookie、Session、Token、JWT
# Cookie
HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息)。
每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。
cookie 存储在客户端。cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。浏览器中有一个专门的文件是用来保存 cookie 的,它保存在硬盘上。
cookie 是不可跨域的。每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的(靠的是 domain)。
如果给某个 cookie 设置了 httpOnly 属性,则无法通过 JS 脚本 读取到该 cookie 的信息,但还是能通过 Application 中手动修改 cookie,所以只是在一定程度上可以防止 XSS 攻击,不是绝对的安全。
与 Cookie 相关的 HTTP 扩展头
Cookie:客户端将服务器设置的 Cookie 返回到服务器。
Set-Cookie:服务器向客户端设置 Cookie。
服务器在响应消息中用 Set-Cookie 头将 Cookie 的内容回送给客户端,客户端在新的请求中将相同的内容携带在 Cookie 头中发送给服务器。从而实现会话的保持。
# Cookie 的作用域
cookie 的作用域是 domain 本身以及 domain 下的所有子域名。
domain 的设置,有两点要注意:
在 setcookie 中省略 domain 参数,那么 domain 默认为当前域名。
domain 参数可以设置父域名以及自身,但不能设置其它域名,包括子域名,否则 cookie 不起作用。
# 跨域携带 Cookie
请求时设置
withCredentials: true
,Request header 中便会带上 Cookie 信息。服务器在响应 header 中设置
Access-Control-Allow-Credentials = true
。
注意
服务器端 Access-Control-Allow-Credentials = true
时,参数 Access-Control-Allow-Origin
的值不能为 *
。
# Session
session 是另一种记录服务器和客户端会话状态的机制。
session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的 cookie 中。
session 认证流程如下:
用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session。
请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器。
浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名。
当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。
# Cookie 和 Session 的区别
安全性: Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
有效期不同:Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
存储大小不同:单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。
# Access Token
访问资源接口(API)时所需要的资源凭证。
简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)。
特点:服务端无状态化、可扩展性好;支持移动端设备;安全;支持跨程序调用。
token 的身份验证流程如下:
客户端使用用户名跟密码请求登录。
服务端收到请求,去验证用户名与密码。验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端。
客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里。
客户端每次向服务端请求资源的时候需要带着服务端签发的 token。
服务端收到请求,然后去验证客户端请求里面带着的 token,如果验证成功,就向客户端返回请求的数据。
每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里。
基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库。
token 完全由应用管理,所以它可以避开同源策略。
# Refresh Token
Refresh Token 是专用于刷新 Acesss Token 的 token。如果没有 Refresh Token,也可以刷新 Acesss Token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 Refresh Token,可以减少这个麻烦,客户端直接用 Refresh Token 去更新 Acesss Token,无需用户进行额外的操作。
Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了。
Refresh Token 及其过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求。
# Token 和 Session 的区别
Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是认证和授权 ,认证是针对用户,授权是针对 App。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID,即认为有此 User 的全部权利。SessionID 是需要严格保密的,这个数据应该只保存在自己的网站,不应该共享给其它网站或者第三方 App。所以简单来说:如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token。如果永远只是自己的网站,自己的 App,用什么就无所谓了。
# JWT
JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案,它是一种认证授权机制。
生成 JWT:jwt.io (opens new window)。
JWT 的认证流程如下:
用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT。
客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)。
当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用 Bearer 模式添加 JWT:
Authorization: Bearer <token>
- 服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为。
因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要。
因为 JWT 并不使用 Cookie,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)。
因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制。
JWT 的使用方式:
- 在请求头的 Authorization 字段中使用Bearer 模式添加 JWT。
Authorization: Bearer <token>
跨域的时候,可以把 JWT 放在 POST 请求的数据体里。
通过 URL 传输。
http://www.example.com/user?token=xxx
# Token 和 JWT 的区别
相同点:
都是访问资源的令牌。
都可以记录用户的信息。
都是使服务端无状态化。
都是只有验证成功后,客户端才能访问服务端上受保护的资源。
不同点:
Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
JWT: 将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
# 常见的前后端鉴权方式
Session-Cookie
Token 验证(包括 JWT,SSO)
OAuth2.0(开放授权)
傻傻分不清之 Cookie、Session、Token、JWT (opens new window)