cookie和session这两个词想必大家都不陌生,在HTTP交互中经常会涉及到这两个概念。但是它们的作用到底是什么?二者之间到底有什么区别与联系呢?
什么是cookie
cookie的定义
根据百度百科的定义:Cookie,类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密)。
用大白话说,就是:
- cookie是由服务器(web应用)生成,保存在客户端(用户浏览器)上的一段数据。
- 该数据用于进行session跟踪。
- 浏览器在后续对同一网站的请求中,都必须带上该cookie。
cookie的用途
因为HTTP是无状态的协议,每一次请求之间都是完全独立的。但为了实现web应用的交互,必须将同一个用户的多次请求关联起来,才能产生有意义的行为。
最典型的例子就是网购时添加商品到购物车,既然HTTP是无状态协议,那么服务器怎么知道商品应该添加到哪个用户的购物车中呢?这就是cookie的作用了。
cookie使用场景1:用户登录
用户成功登录电商网站后,服务器会生成一个登录凭据,并通过response header中的 Set-Cookie 字段将其返回给用户浏览器,通知其保存该cookie信息。
# 用于举例,实际的cookie是经过加密的
Set-Cookie: uid=1
浏览器一般默认会将该cookie保存在用户电脑某个目录下的一个文本文件中,后续对该网站发起的request header中,都会携带该cookie。
这样服务器在收到用户请求的同时,也可以从请求携带的cookie中知道该请求是来自哪个用户了。这样就实现了用户登录状态的保持。
cookie使用场景2:购物车商品添加
用户添加商品A到其购物车时,服务器可以修改cookie字段,将商品A的信息追加到cookie中,这样就实现了将商品A与用户关联到一起的行为。
Set-Cookie: uid=1;prod=A
当用户又添加了商品B到购物车中时,服务器又会将商品B的信息追加到cookie中。
Set-Cookie: uid=1;prod=A; prod=B
等到用户结账的时候,服务器就可以从请求携带的cookie中获取到该用户选择的所有商品信息。
什么是session
我们常说的session,实际上有两层概念。
首先是它的抽象概念:在用户端与服务端之间一对一连续的交互,抽象为一个用户会话,也就是session。比如从用户登录到登出之间进行的一系列交互,都属于同一个session。
其次是它的实际概念:为了实现用户会话的保持,服务端通过创建session对象记录了用户登录后的各种信息,比如用户名,用户的操作等等。
session是服务端上对一个请求用户进行标识的唯一信息,并将session的信息通过cookie返回给用户,这样就实现了在用户端和服务端对用户会话的跟踪。
cookie和session的区别与联系
-
cookie是保存在客户端的,比如用户电脑上,通常是保存在一个文本中;而session是保存在服务端的,通常保存在数据库或者缓存中,也可以保存在文件系统中。
-
cookie是HTTP协议中的概念,位于request header中,服务器可以通过response header中的Set-Cookie字段设置和修改返回给客户端的cookie;而session是一个服务端层面的概念,不通的编程语言可能有不同的实现方式,其目的都是为了记录保持用户的状态。
-
我们常说的session会话保持,实际上就是通过借助cookie的特性,将服务端保存的session状态返回给客户端,并在整个用户会话过程中通过cookie进行持续的交互,进而实现抽象层面的session会话保持。
所以说cookie本身并没有和session有必然的联系,它只是我们用来实现session跟踪的一种常用手段。
Django中的session实现
Django是通过中间件的形式支持session的。在创建项目时默认就会在settings.py中配置好session中间件。
INSTALLED_APPS = [
'django.contrib.sessions',
]
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
]
Django默认会将session信息保存在数据库中,可以通过 manage.py migrate 生成session信息表。如果对性能要求比较高,也可以将session信息配置保存到缓存中。
mysql> desc django_session;
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| session_key | varchar(40) | NO | PRI | NULL | |
| session_data | longtext | NO | | NULL | |
| expire_date | datetime(6) | NO | MUL | NULL | |
+--------------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
可以看到,django_session表中有3个字段,其中:
session_data 实际对用户信息进行加密后生成的密文,可以解密;
session_key是根据session_data密文生成的一串随机字符串,本身并无实际意义,仅用于插入到cookie中返回给客户端,并在用户发起请求时对请求中携带的cookie与django_session表中的session_key进行匹配,查询是否有匹配的session_data;
expire_date记录了该session的过期时间。
实践是检验真理的唯一标准
在发起登录请求后,服务端在response header中携带了Set-Cookie字段,内容包含了csrftoken,以及本次会话的sessionid。
(cookie中的csrftoken机制请见 CSRF攻击与Django防范)
Set-Cookie: csrftoken=ATfjCEJZVaN9zTYnSHPzSRJhWfVfFlxntOYyu2f64j4mf9clzigVxsBIFN293uu3; expires=Sun, 28 Feb 2021 14:58:22 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Set-Cookie: sessionid=j0b9jsiiumwfjzmt9k3tw05aw7q5xk2n; expires=Sun, 15 Mar 2020 14:58:22 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
在Django数据库中,可以看到django_session表中也有对应的记录
在登录后的下一次请求中,可以看到request header中的cookie中已经携带了服务端返回的sessionid
至此,通过服务端的session机制,并借助HTTP协议的cookie机制,就实现了HTTP无状态协议的会话保持,用户也就可以进行愉快的页面交互了。