文章

HTTP 基础/连接/授权

基础

请求方法

请求方法 Body 幂等
GET 获取资源 Y
POST 增加或修改资源 Y
PUT 修改资源 Y Y
DELETE 删除资源 Y
HEAD 同 GET;响应报文没有 Body 响应报文也没有 Y

状态码

详细列表

状态码 描述
1xx 临时消息
100 继续发送
101 协议切换 e.g. http1.1 -> http2
2xx 成功
3xx 重定向
4xx 客户端错误
5xx 服务器错误
  • Header: 用于主机确定子主机,不用于 DNS 解析。

  • Content-Length: Body 长度。Body 可能包含任意数据,包括二进制数据。因此难以设置一个结束标记。所以需要显式指明数据长度。

  • Content-Type:

    • text/html: html 文本,用于浏览器页面响应。

    • application/x-www-form-urlencoded: 普通表单(纯文本表单),URL encoded 格式。

      [body]
      key1=v1&key2=v2
      
    • multipart/form-data: 多部分形式。多用于传输包含二进制项的多项数据。

      POST /users HTTP/1.1
      Content-Type: multipart/form-data; boundary=----WebKitFormBoundary777777
      
      ----WebKitFormBoundary777777
      ......
      
    • application/json

    • application/zip, image/jpeg...: 单文件,多用于响应或 POST/PUT 请求。

  • Location: 重定向目标 URL

  • Range: 指定 Body 内容范围。用于分段加载/断点续传。服务器用 Accept-Ranges 指明是否支持,以及支持的单位。

    Range: bytes=0-1024
    
  • Cookie / Set-Cookie

  • Authorization

登录与授权

Authorization

Authorization 是一个 HTTP Header,根据具体使用的规范不同,携带不同的数据来认证。

Basic

Header 格式:

Authorization: Basic xxxxxx(<username:password> 整体 base64 编码)

缺点

  • 明文传输 (http) 易泄漏密码。
  • 一旦泄漏不易撤销授权(需要改密码)。

Bearer

Header 格式:

Authorization: Bearer <token>

此处的 Token 一般来源于:

  • 第三方授权提供(OAuth),用于向 Provider 请求用户数据。
  • 自家服务生成,用于客户端与服务器通讯。

OAuth2

在场景 「用户使用 Github 登录掘金」 中:

  • 第三方登录:Github 为第三方。(这是一个不规范的名词)
  • 第三方授权:掘金为第三方。本质是 Github 授权掘金获取用户的数据。
sequenceDiagram
autonumber
participant S as 3rd-party Server
participant B as 3rd-party Client
participant P as Provider

B->>P: client_id
P->>B: authorization_code
B->>S: authorization_code
S->>P: authorization_code & client_secret
P->>S: access_token & refresh_token
  • 跳转到 Provider 的网站。client_id 用于 Provider 确认第三方身份。

  • ②: OAuth 不强制使用 https(也无法保证浏览器本身不是恶意软件),故这里只返回 code 而不是 token,防止被窃取。

    AuthCode 换取 token 的时候需要 client_secret,所以即使 AuthCode 被窃取也依然安全。

  • ⑤: 第三方得到 token,OAuth2 流程结束。 refresh_token 出于安全目的提供,防止 access_token 泄漏。可以立即刷新 access_token,或在其过期后自助重新获取。

原则上 access_token 不应该发送到客户端,客户端不应该自己去 Provider 取数据。

TCP/IP 协议族

分层

  • 应用层/Application Layer

  • 传输层/Transport Layer:提高传输稳定性,为应用层提供数据切分分块传输/重传能力。

  • 网络层/Internet Layer:不是所有的场景都需要重传,但它们都需要网络(寻址/路由等),因此抽象出网络层。

  • 数据链路层/Link Layer:不同的物理设备/传输介质需要不同的协议,因此抽象出数据链路层。

TCP 连接

建立连接

TCP 是有状态协议,需要建立连接。为了节省资源,也需要关闭连接。

sequenceDiagram
autonumber
participant C as Client
participant S as Server

C->>S: SYN=1
activate C
Note left of C: SYN_SEND

S->>C: SYN=1, ACK=1
deactivate C
activate S
Note right of S: SYN_RCVD

C->>S: SYN=0, ACK=1, DATA
deactivate S
Note left of C: ESTABLISHED
Note right of S: ESTABLISHED

关闭连接

sequenceDiagram
autonumber
participant C as Client
participant S as Server

C->>S: FIN=1
activate C
Note left of C: FIN_WAIT_1

S->>C: ACK=1
deactivate C
activate S
activate C
Note right of S: CLOSE_WAIT <br/>半关闭:客户端不再发送数据
Note left of C: FIN_WAIT_2

S->>C: FIN=1, ACK=1
deactivate C
deactivate S
activate S
Note right of S: LAST_ACK<br/>服务端不再发送数据

C->>S: ACK=1
deactivate S
activate C
activate S
Note right of S: CLOSED
Note left of C: TIME_WAIT<br/>2MSL
deactivate C
activate C
Note left of C: CLOSED
deactivate C
deactivate S

长连接

TCP 连接若长时间空闲,网关或中间的其他网络节点可能会强行关闭来释放资源。

但实际上不发消息不代表打算关闭连接,随时有可能出现新消息。

所以我们要定期发送一点消息来保持活跃,称为「心跳包」。

心跳包的频率等问题,需要在稳定/耗电等诸多方面寻求平衡,是一个专门的话题。

HTTPS 连接

HTTPS 本质是在 HTTP 与 TCP 直接插入一个加密层。它在客户端与服务器之间用非对称加密协商出一个对称加密密钥,用这个密钥加密后续的数据。

HTTPS (TLS) 也是有状态的(比如协商出的对称密钥),同样需要连接。

基本流程

  1. 客户端请求建立 TLS 连接
  2. 服务器发回证书
  3. 客户端验证证书
  4. 协商对称密钥
  5. 开始加密通讯

sequenceDiagram
autonumber
participant C as Client
participant S as Server

C->>S: Client Hello 0x01 etc..
S->>C: Server Hello 0x02 etc.. 
S->>C: Cert etc..
C->>S: Pre-master secret (服务器公钥加密)
C->>S: Finish
S->>C: Finish
  • ①: 除了握手包,也包括可选的 TLS 版本 / 加密套件 Cipher suites(可选的对称/非对称/hash 算法)/ 一个随机数。

  • ②: 除了服务端握手包,还包括选定的 TLS 版本 / 加密套件 / 一个服务端生成的随机数。

  • ③: 服务器发送:

    1. 自己的证书(包括:公钥/主机名/签名等)
    2. 用于签名的私钥对应的证书(证书签发机构的证书)
    3. ...
    4. 根 CA 的信息
  • ④: 发送后。双方都可以使用 Pre-master-secret + Client-random + Server-random 计算出 Master-secret。

    为什么不直接用 pre-master 作为密钥?

    防止重放攻击 (replay attack)

    之后,双方也都可以通过 Master-secret 推算出这些数据:

    • 客户端密钥
    • 服务端密钥
    • 客户端 MAC secret
    • 服务端 MAC secret

    为什么 TLS 中客户端与服务端要使用不同的密钥?

    防止黑客把客户端的数据原样发回。此消息密码正确,会被误认为是服务器返回的。

    什么是 MAC secret?

    HMAC: hash-based message anthenticate code. 在 hash 的过程中添加进去一个密钥,一遍确认执行 hash 的人身份。

  • ⑤: 客户端通知服务器即将发送加密信息;将上面几步的数据结合在一起加密/HMAC 发送。

  • ⑥: 服务器通知客户端即将发送加密信息;将上面几步的数据结合在一起加密/HMAC 发送。