导航
本文是学习青训营课程《HTTP实用指南》,平时刷题以及做项目时遇到的HTTP/HTTPS
问题的应用和总结。主要有以下四点:
HTTP
与HTTPS
异同HTTP
1.0
/1.1
/2.0
WebSocket
简单入门Cookie
、Session
、Token
、JWT
- 应用场景:
XMLHttpRequest()
,axios
,Taro.request()
HTTP 与 HTTPS 异同
HTTPS
(443端口) 经由HTTP
(80端口)进行通信,但利用SSL/TLS
来加密数据包。HTTP
协议则以明文方式发送内容。HTTP
建立过程
- 先通过
DNS
将域名转换为IP
地址。即将 test.com 转换为 221.239.100.30 这一过程- 通过三次握手(稍后会讲)建立 TCP 连接。
- 发起 HTTP 请求。
- 目标服务器接收到 HTTP 请求并处理。
- 目标服务器往浏览器发回 HTTP 响应。
- 浏览器解析并渲染页面
- 往返时延(Round-Trip Time): 表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延
HTTPS
建立过程
- 先通过
DNS
将域名转换为IP
地址。即将 test.com 转换为 221.239.100.30 这一过程- TCP 三次同步握手
- 客户端验证服务器数字证书
- DH 算法协商对称加密算法的密钥、hash 算法的密钥
- SSL 安全加密隧道协商完成
- 网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的hash算法进行数据完整性保护,保证数据不被篡改
HTTP 1.0/1.1/2.0
a. HTTP 1.0
- 增加了 Header
- 有了状态码
- 支持多种文档类型
b. HTTP 1.1
- 链接复用
- 缓存
- 内容协商
缺点:
1.
TCP
队头阻塞:若某响应一直未能完成,那后面所有的请求就会一直阻塞着。一次只能处理一个请求或响应,完成之前不能停止解析。无法预判解析需要多少内存。2.
TCP
低效,慢启动机制3. 头部臃肿:首部无法压缩;cookie的存在。
4. 优先级设置受限:HTTP/1.1 无法为重要的资源指定优先级,每个 HTTP 请求都是一视同仁。
c. HTTP 2.0
HTTP/2 是基于帧的协议。采用分帧是为了将重要信息封装起来,让协议的解析方可以轻松阅读、解析并还原信息。帧---> 消息 ---> 数据流
- 二进制协议:二进制分帧层,所有的请求和响应都在同一个 TCP 连接上发送【客户端和服务器把 HTTP 消息分解成多个帧,然后乱序发送,最后在另一端再根据流 ID 重新组合起来。】
并行交错发送多个请求和响应, 消除不必要的延迟
- 压缩header:相同首部存储
- 服务器推送:服务器可以对一个客户端请求发送多个响应。除了对最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确地请求。
- 多路复用:分帧,请求和响应都可以多路复用,有助于解决类似类似队头阻塞的问题。
- 优先级:
HEADERS
,PRIORITY
- 流量控制: 双向字节流,阻止发送方向接收方发送大量数据的机制。
WebSocket 简单入门
WebSocket
基于 TCP/IP
协议,存在于应用层,是HTML5
新特性.
- 在tcp协议之上只需要一次握手[GET请求],就可以建立客户端和服务器的连接
- 可发送文本,也可发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信
- 请求URL:
ws://example.com:12345/path
或者wss://example.com:12345/path
;其中,ws
对应WebSocket
协议,wss
对应WebSocket Secure
协议,后者相当于HTTPS
实用开源项目
Cookie、Session、Token、JWT
Authentication
:认证,验证当前用户的身份。表单登录。Authorization
:授权,用户授予第三方应用访问该用户某些资源的权限。实现授权的方式有:cookie
、session
、token
、OAuth
。Credentials
:凭证,实现认证和授权的前提是需要一种媒介(证书)。令牌(token
)
鉴权方式
- Session-Cookie
- Token 验证(包括 JWT,SSO)
- OAuth2.0(开放授权)
a. Cookie
cookie
存储在客户端,不可跨域,一级域名和二级域名之间允许共享使用的。- 重要属性:
maxAge
最常用,expires
过期时间,secure
是否经过HTTPS
,httpOnly
只读不能修改【可以防止 XSS 攻击】 - 告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过
cookie
或者session
去实现。即,记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。 - 单个
Cookie
保存的数据不能超过4K
,可长时间保持,只支持字符串
b. Session
- session 记录服务器和客户端会话状态。即,记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。
- 基于
cookie
实现的,但存在服务端,sessionId
会被存储到客户端的cookie
中。SessionID
是连接Cookie
和Session
的一道桥梁 - 比
Cookie
安全,可以存任意数据类型,但有效期短一点【客户端关闭(默认情况下)或者Session
超时都会失效】 Session
可存储数据远高于Cookie
,但是当访问量过多,会占用过多的服务器资源。
c. Token
可以避免 CSRF 攻击,因为此时不需要使用 Cookie
- Acesss Token
访问资源接口(API)时所需要的资源凭证
- 简单 token 的组成:
uid
(用户唯一的身份标识)、time
(当前时间的时间戳)、sign
(签名,token
的前几位以哈希算法压缩成的一定长度的十六进制字符串) - 每一次请求都需要携带
token
,需要把token
放到HTTP
的Header
。 - 服务端不用存放
token
数据 - 完全由应用管理,所以它可以避开同源策略
- 服务端无状态化、可扩展性好
- 支持移动端设备
- 安全
- 支持跨程序调用
- Refresh Token
专用于刷新 access token
的 token
。有了它就不用每次刷新都要用户输入登录用户名与密码。
d. JWT
JSON Web Token
(简称 JWT
)是目前最流行的跨域认证解决方案。- 是一种认证授权机制。
Token 和 JWT 的区别:
相同:
- 都是访问资源的令牌
- 都可以记录用户的信息
- 都是使服务端无状态化
- 都是只有验证成功后,客户端才能访问服务端上受保护的资源
区别:
- Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
- JWT: 将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据
应用场景
- 移动端一般选择 token
- JWT 要使用 HTTPS 协议传输
- 扫码登录基于二维码原理与token的认证机制
应用场景
平时写项目用的最多的还是POST
和GET
请求。做题经常遇到XMLHttpRequest()
; PC端项目则使用axios
,工程化中会有较为复杂的axios
二次封装,比如设置请求拦截器和响应拦截器;在小程序项目中,则使用Taro
框架提供的方法Taro.request()
,在工程化中同样也会采取二次封装的形式用于处理复杂指令。以下为简单的实现应用。
a. XMLHttpRequest()
const readUrlPromise = url => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url) // 准备发送请求
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
setTimeout(() => {
resolve({ data: JSON.parse(xhr.responseText) })
}, 3000)
} else if (xhr.readyState === 4 && xhr.status !== 200) {
reject('请求失败!')
}
}
xhr.onerror = function () {
reject('请求失败')
}
xhr.send(null) // 数据可以通过send携带过去,send是通过请求体传数据的
cancelFn = function (msg) {
reject(msg)
}
})
}
b. axios
axios.create(config)
根据指定的配置条件(例如接口地址baseURL)创建一个新的axios
- 请求拦截器:成功则返回
config配置
,失败则返回Promise.reject(error)
- 响应拦截器:成功则返回
response结果
,失败则返回Promsie.reject(error)
- 取消请求:
axios.CancelToken
- 结合
TypeScript
封装接口
// const strictFetch = Axios.create(DEFAULT_OPTIONS) 接口配置
// axiosInstance.interceptors.request.use(config()=>{}, error=>{}) 请求拦截器
// axiosInstance.interceptors.response.use(config()=>{}, error=>{}) 响应拦截器
// 常见考题 Promise控制并发数
function limitRequest(urls, limit) {
return new Promise((reslove) => {
const len = urls.length
let count = 0
// 同时启动limit个任务
while (limit > 0) {
start()
limit --
}
function start() {
const url = urls.shift() // 从数组中拿去第一个任务
if (url) {
axios.post(url)
.then(res => {
// todo
}).catch(err => {
// todo
}).finally(() => {
if (count === len - 1 ) {
// 完成最后一个任务
reslove()
} else {
count ++
start() // 任务完成之后启动下一个任务
}
})
}
}
})
}
// ts简单封装
interface API {
'url': {
id: number,
},
//...
}
function request<T extends keyof API>(url: T, obj: API[T]) {
return axios.post(url, obj)
}
c. Taro.request()
Taro
的用法也非常简单。它支持返回的数据格式包括json
, text
, base64
, arraybuffer
; 最常用的为json
,其他三个在不同端上有支持度差别。
const callBackRequest = Taro.request({
url: '', // 接口地址
data: {}, // 请求参数
method: 'POST',
header: { 'content-type': 'application/json' },
success: (res: any) => {
// 此处处理复杂操作 fn()
console.log(res)
}
})
// 对于简单的请求任务
const res = await Taro.request(params)