HTTP/HTTPS学习笔记与应用 | 青训营

57 阅读8分钟

导航

本文是学习青训营课程《HTTP实用指南》,平时刷题以及做项目时遇到的HTTP/HTTPS问题的应用和总结。主要有以下四点:

  1. HTTPHTTPS 异同
  2. HTTP 1.0/1.1/2.0
  3. WebSocket 简单入门
  4. CookieSessionTokenJWT
  5. 应用场景:XMLHttpRequest(), axios, Taro.request()

HTTP 与 HTTPS 异同

  1. HTTPS(443端口) 经由 HTTP (80端口)进行通信,但利用 SSL/TLS 来加密数据包。HTTP 协议则以明文方式发送内容。
  2. HTTP建立过程
  1. 先通过DNS将域名转换为 IP 地址。即将 test.com 转换为 221.239.100.30 这一过程
  2. 通过三次握手(稍后会讲)建立 TCP 连接。
  3. 发起 HTTP 请求。
  4. 目标服务器接收到 HTTP 请求并处理。
  5. 目标服务器往浏览器发回 HTTP 响应。
  6. 浏览器解析并渲染页面
  • 往返时延(Round-Trip Time): 表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延
  1. HTTPS建立过程
  1. 先通过DNS将域名转换为 IP 地址。即将 test.com 转换为 221.239.100.30 这一过程
  2. TCP 三次同步握手
  3. 客户端验证服务器数字证书
  4. DH 算法协商对称加密算法的密钥、hash 算法的密钥
  5. SSL 安全加密隧道协商完成
  6. 网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的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:相同首部存储
  • 服务器推送:服务器可以对一个客户端请求发送多个响应。除了对最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确地请求。
  • 多路复用:分帧,请求和响应都可以多路复用,有助于解决类似类似队头阻塞的问题。
  • 优先级HEADERSPRIORITY
  • 流量控制: 双向字节流,阻止发送方向接收方发送大量数据的机制。

WebSocket 简单入门

WebSocket 基于 TCP/IP 协议,存在于应用层,是HTML5新特性.

  • 在tcp协议之上只需要一次握手[GET请求],就可以建立客户端和服务器的连接
  • 可发送文本,也可发送二进制数据。
  • 没有同源限制,客户端可以与任意服务器通信
  • 请求URLws://example.com:12345/path 或者 wss://example.com:12345/path;其中,ws对应WebSocket协议,wss对应WebSocket Secure协议,后者相当于HTTPS

实用开源项目

  1. github.com/robtaussig/… 在线演示:robtaussig.com/socket
  2. 如果是MAC选手:github.com/vi/websocat

Cookie、Session、Token、JWT

  • Authentication:认证,验证当前用户的身份。表单登录。
  • Authorization:授权,用户授予第三方应用访问该用户某些资源的权限。实现授权的方式有:cookiesessiontokenOAuth
  • Credentials:凭证,实现认证和授权的前提是需要一种媒介(证书)。令牌(token

鉴权方式

  1. Session-Cookie
  2. Token 验证(包括 JWT,SSO)
  3. OAuth2.0(开放授权)

a. Cookie

  • cookie 存储在客户端不可跨域,一级域名和二级域名之间允许共享使用的。
  • 重要属性: maxAge最常用,expires过期时间,secure是否经过HTTPShttpOnly只读不能修改【可以防止 XSS 攻击
  • 告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。即,记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。
  • 单个 Cookie 保存的数据不能超过 4K,可长时间保持,只支持字符串

b. Session

  • session 记录服务器和客户端会话状态。即,记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。
  • 基于 cookie 实现的,但存在服务端sessionId 会被存储到客户端cookie 中。SessionID 是连接 CookieSession 的一道桥梁
  • Cookie 安全,可以存任意数据类型,但有效期短一点【客户端关闭(默认情况下)或者 Session 超时都会失效】
  • Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。

c. Token

可以避免 CSRF 攻击,因为此时不需要使用 Cookie

  1. Acesss Token

访问资源接口(API)时所需要的资源凭证

  • 简单 token 的组成:  uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
  • 每一次请求都需要携带 token,需要把 token 放到 HTTPHeader
  • 服务端不用存放 token 数据
  • 完全由应用管理,所以它可以避开同源策略
    • 服务端无状态化、可扩展性好
    • 支持移动端设备
    • 安全
    • 支持跨程序调用
  1. Refresh Token

专用于刷新 access tokentoken。有了它就不用每次刷新都要用户输入登录用户名与密码。

d. JWT

JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。- 是一种认证授权机制

Token 和 JWT 的区别:

相同:

  • 都是访问资源的令牌
  • 都可以记录用户的信息
  • 都是使服务端无状态化
  • 都是只有验证成功后,客户端才能访问服务端上受保护的资源

区别:

  • Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
  • JWT: 将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据

应用场景

  1. 移动端一般选择 token
  2. JWT 要使用 HTTPS 协议传输
  3. 扫码登录基于二维码原理与token的认证机制

应用场景

平时写项目用的最多的还是POSTGET请求。做题经常遇到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

  1. axios.create(config) 根据指定的配置条件(例如接口地址baseURL)创建一个新的axios
  2. 请求拦截器:成功则返回config配置,失败则返回Promise.reject(error)
  3. 响应拦截器:成功则返回response结果,失败则返回 Promsie.reject(error)
  4. 取消请求:axios.CancelToken
  5. 结合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)

参考资料

  1. 青训营课程:《HTTP实用指南》
  2. github.com/woai3c/Fron…
  3. juejin.cn/post/684490…
  4. Taro:docs.taro.zone/docs/apis/n…