HTTP & Session & Cookie

827 阅读7分钟

导读

本文主要梳理了HTTP、Session、Cookie这三个常见的术语的关系,通过本文你可以清楚的了解以下几个问题的答案

  1. HTTP无状态(Stateless)具体指的是什么
  2. Session是什么以及与HTTP的关系
  3. Session和Cookie的关系
  4. Cookie的相关知识

无状态的HTTP

HTTP 超文本传输协议,主要是为了Web客户端与Web服务器之间的通信而设计的,意即客户端和服务器按照该协议指定的形式,可以进行数据交换。

指定的格式就是报文的格式,此处不展开。

简单的理解就是,在客户端按照发送一个HTTP请求到指定服务器,服务器接收到请求之后会返回对应的数据。现代的web应用就是基于此,组装多次HTTP请求实现的。

HTTP的无状态指的是,在协议中并没有关于请求身份识别的规定。

对于服务器来说,同一个客户端的多次请求之间是完全独立的。

这些请求之间不会有数据的共享。当前的请求,与之前的请求以及后续的请求没有任何数据上的关系,服务器对每一次请求都看作是一个全新的,不会说接收到一个请求之后,通过某种识别方式与其他次的请求关联上。

服务器就像是一条没有记忆的鱼,来了请求就处理,完全不会去识别请求的身份。

最初的时候,web网站主要是展示静态页面,无状态的HTTP协议完全可以胜任。但是随着发展,web网站产生了越来越复杂的交互,需要多次请求协同来构建网站数据,这时候无状态的话就乱套了。

想象一下无状态情况下,你在购物网站一通选购,结果结账的时候发送请求,服务器就懵逼了,你是谁,你选了啥,用什么支付,这就乱套了。

这时候就需要Session出场了。

使HTTP有状态的Session

Session是什么?

关于Session的含义很不确切,该单词的表面意思是会话,实在是太抽象了,关于有一份相关的RFC2109中对Session有一个比较合适的描述

This document describes a way to create stateful sessions with HTTP requests and responses. Currently, HTTP servers respond to each client request without relating that request to previous or subsequent requests; the technique allows clients and servers that wish to exchange state information to place HTTP requests and responses within a larger context, which we term a "session".

这段话对Session的定义是,本质上是一个大的上下文环境,在这个上下文中客户端和服务器可以进行有状态的交互。

因此可以定义一下,Session是一个解决方案,可以使得客户端和服务器在一个上下文的生命周期(简单理解为一段时期内)内,以有状态的形式交互(多次请求之间可以做数据状态共享)

Session的实现方案

了解了Session的目的之后,那么就可以设计方案来实现。

We outline here a way for an origin server to send state information to the user agent, and for the user agent to return the state information to the origin server.

这句话是说创建session的一个思路就是,设计一个方案,源服务器发送状态信息给客户端,客户端再将该状态信息返回给源服务器。

一言以蔽之就是构建一个在服务器和客户端之间的状态信息流转机制

本质上就是在多次请求之间维护一个状态数据,服务器以此来识别请求的身份。

因此Session的构建,可以有以下几种方式

  • 利用url查询字符串
  • 利用form表单的隐藏字段
  • 利用IP地址
  • 利用Cookie

但是前3种都会有这样那样的问题,

url查询字符串显式暴露、在页面前进后退时需要复杂的处理逻辑;form表单隐藏字段,需要额外的维护成本;ip地址不安全,且很容易被伪造;并且这些方式都与HTTP协议是割裂的,某种程度上算是一种hack。

因此在RFC2109中提出了,在报文头中增加一对报文头,通过这对报文头来实现。这对报文头就是常见的Set-CookieCookie

The origin server initiates a session, if it so desires. To initiate a session, the origin server returns an extra response header to the client, Set- Cookie.

A user agent returns a Cookie request header (see below) to the origin server if it chooses to continue a session.

The origin server may ignore it or use it to determine the current state of the session. It may send back to the client a Set-Cookie response header with the same or different information, or it may send no Set-Cookie header at all. The origin server effectively ends a session by sending the client a Set-Cookie header with Max-Age=0.

Servers may return a Set-Cookie response headers with any response. User agents should send Cookie request headers, subject to other rules detailed below, with every request.

An origin server may include multiple Set-Cookie headers in a response. Note that an intervening gateway could fold multiple such headers into a single header.

# 响应头
Set-Cookie: name=value; max-age=delta-seconds,otherName=value;max-age=delta-seconds 
Set-Cookie: name3=value; max-age=3600; domain=/

# 请求头
Cookie: name1=value; name2=value

目前最常见的就是利用Cookie来构建Session了。

报文头是HTTP协议的一部分,HTTP协议是规范客户端和服务端的交互方式的。可以理解为客户端和服务器会按照协议中指定的方式来解析报文。

利用Cookie构建Session

有了这两个字段之后,只要服务器在响应头上设置一个Set-Cookie字段,那么就会有一段文本数据从服务器传递到客户端;客户端接收到响应之后,按照HTTP规范会解析响应头,识别到该字段之后,会把字段值按照格式保存在本地;然后在后续的请求中,会自动的把符合条件的Cookie信息添加到请求头中,返回给服务器。【这样就会有一段数据状态在服务器和客户端之间流转了】。

注意: 上述行为是HTTP规范设计的,各浏览器(用户代理)约定遵守的行为,不需要开发者额外做工作。因此说这种方式可以说是以HTTP协议化实现的,不那么hack。

一个简单的例子:

1.  User Agent -> Server

         POST /acme/login HTTP/1.1
         [form data]

         User identifies self via a form.
         
2.  Server -> User Agent

         HTTP/1.1 200 OK
         Set-Cookie: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"

         Cookie reflects user's identity.

3.  User Agent -> Server

         POST /acme/pickitem HTTP/1.1
         Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
         [form data]

         User selects an item for "shopping basket."

4.  Server -> User Agent

         HTTP/1.1 200 OK
         Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
                 Path="/acme"

         Shopping basket contains an item.

5.  User Agent -> Server

         POST /acme/shipping HTTP/1.1
         Cookie: $Version="1";
                 Customer="WILE_E_COYOTE"; $Path="/acme";
                 Part_Number="Rocket_Launcher_0001"; $Path="/acme"
         [form data]

         User selects shipping method from form.

以上就是利用cookie实现Session的实现方式: 共享的状态数据以cookie的形式保存在客户端,并且在客户端和服务器之间流转。

但是这些共享的状态数据是存储在cookie中,会引起一些问题:

  1. cookie本身是有大小限制的,4KB,复杂应用的话这个容量可能不够;
  2. 本地的cookie有被篡改的风险,会产生安全性问题

因此,在此基础上出现了一种新的方案: 服务器在设置Set-Cookie的时候,不把共享的那些状态数据作为cookie的value值传递给客户端,而是在服务器上开辟一段空间存储,然后把存储地址作为cookie的value返回给客户端。 这样客户端与服务器之间流转的只是一段地址信息;这样以来避免了cookie空间不足的问题,另一方便服务器数据的安全也比本地高很多,不容易被篡改。

第二种方式常见的做法就是在Set-Cookie: SessionId=XXXX;这个SessionId的名称也是造成session概念混乱的一个原因。

结束

以上就是HTTP、Session、Cookie这三个概念的关系。