Cookie & Session
由于HTTP协议的无状态性,每次连接结束后HTTP连接都会自动断开,因此每一个来自客户端浏览器的请求都是独立的。由于无法保存用户的访问状态,后端服务无法判断当前请求和之后的请求是不是来自同一个用户。对于静态网站而言这并不是什么问题,但是对于动态Web应用,无法识别用户身份并保持用户状态则时致命的,因为根本无法提供服务。
比如,用户必须经过登录之后才能使用Web应用提供的功能,用户首次通过账户和密码登录成功后,对于HTTP而言是无法将认证成功的用户状态通过协议层面进行保存的。那么当用户再次再次请求时,Web服务器如何得知该请求是哪个用户发起的呢?
为了保持HTTP连接状态,Web服务器会通过客户端浏览器在用户本地机器磁盘上限定的位置写入用户相关的数据,这也就是所谓的Cookie。通过Cookie可以保存比如用户名、浏览记录、表单记录、登录注销等数据。但这种方式最大的弊端是非常不安全,由于Cookie是保存在用户机器上的,如果这些Cookie信息被伪造或串改或是删除,则会造成极大地安全隐患。
Cookie的工作流程
- 客户端向服务器发起一个HTTP请求建立连接
- 服务器在HTTP响应头中添加
Set-Cookie
的键值对 - 客户端在随后的HTTP请求头中添加Cookie首部,内容包含之前服务器响应中设置的Cookie信息。
- 服务端根据Cookie首部获取用户信息
Set-Cookie:sessionid=a92d67e44a9b92d7dafca67e507985c0;
expires=Thu, 07-Jul-2011 04:16:28 GMT;
Max-Age=1209600;
Path=/
因此,现代浏览器使用Cookie只会保存一些对用户而言不太重要的数据,实际的用户状态等数据则会以Session会话的形式保存在服务端。简单来说Session就是服务端的Cookie。也就是将用户数据和状态保存在远程服务器中,这远比保存在客户端更加安全、方便和快捷。
服务端的Session中会存储特定用户会话所需的属性和配置信息,当用户在应用程序的页面之间跳转时,储存在Session中的数据将不会丢失,一直会在用户会话过程保存。因此,Session会话可以看作是客户端与服务端沟通的一种工具。
服务端的Session是需要依赖于客户端的Cookie的,与Cookie不同的Session将重要的用户状态数据保存在服务端,而Cookie只会保存一些非明文的识别信息,比如哈希值。
为什么说Session是服务端的Cookie呢?
Session的底层是基于Cookie技术来实现的,在早期存储会话状态一直使用的是Cookie,客户端的Cookie中保存着明文的键值对,由于直接保存重要的用户数据是不安全的,其后才衍生出Session。Session是加密后的Cookie键值。
当用户通过客户端浏览器发送请求访问远程Web服务器应用程序指定的页面时,如果客户端还没有会话,Web服务器会自动为其创建一个Session会话对象,并且为每个Session对象分配一个唯一的sessionid
。Session对象创建成功后,Web服务器通过传递一个名字为SESSIONID
的Cookie将这个sessionid
返回给浏览器。
当用户再次访问Web服务器时,会携带上具有sessionid
的Cookie一并发送给Web服务器。Web服务器通过不同的sessionid
来查询出与之对应的Session对象,通过Session对象为用户提供不同的服务。如果当用户会话过期或被放弃后,Web服务器也会终止该会话。
比如:PHP中利用Cookie来管理Session的方式
Django Session
Django内置了通用的Session会话框架,并提供了多种Session数据保存的方式,分别是数据库、缓存、文件、缓存+数据库、加密Cookie。Djaong默认会将Session保存到名为django_session
的数据表中。没有特别要求,尽量不要将Session保存到Cookie之中。如果从性能能上考虑的话,基于缓存的会话会更优。
启用Session
在Django项目中启用Session框架
- 在项目配置的
MIDDLEWARE_CLASSES
中间件类中添加SessionMiddleware
会话中间价 - 在项目配置的
INSTALLED_APPS
注册sessions
框架
Django通过内置的SessionMiddleware
中间件实现会话功能,要启动会话就必须先开启该中间件。
$ vim settings.py
MIDDLEWARE_CLASSES = [
"django.contrib.sessions.middleware.SessionMiddleware"
]
INSTALLED_APPS = [
"django.contrib.sessions"
]
Session配置
在项目配置文件中可以添加会话配置选项
会话配置 | 默认值 | 描述 |
---|---|---|
SESSION_ENGINE | django.contrib.sessions.backends.db | 设置Session引擎 |
SESSION_FILE_PATH | None | 缓存Session文件的路径 |
SESSION_COOKIE_NAME | sessionid | Session在浏览器中存储的键名 |
SESSION_COOKIE_PATH | / | Session应用的路径默认为全站 |
SESSION_COOKIE_DOMAIN | None | Session应用的站点域名 |
SESSION_COOKIE_SECURE | False | 是否允许使用HTTPS传输Cookie |
SESSION_COOKIE_HTTPONLY | True | 是否仅能使用HTTP传输Session的Cookie |
SESSION_COOKIE_AGE | 43200 | Session的存活时长 |
SESSION_EXPIRE_AT_BROWSER_CLOSE | True | 关闭浏览器时是否清除Session |
SESSION_SAVE_EVERY_REQUEST | False | 每次请求是否重新生成并保存Session |
Session引擎
- 基于数据库的会话
SESSION_ENGINE = django.contrib.sessions.backends.db
默认Django将会话数据保存在数据库,并通过django.contrib.sessions.models.Session
模型创建django_session
数据表。
CREATE TABLE `django_session` (
`session_key` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`session_data` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`expire_date` datetime(6) NOT NULL,
PRIMARY KEY (`session_key`),
KEY `django_session_expire_date_a5c62663` (`expire_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
- 基于缓存的会话
SESSION_ENGINE = django.contrib.sessions.backends.cache
- 基于数据库与缓存的会话
SESSION_ENGINE = django.contrib.sessions.backends.cache_db
- 基于文件的会话
SESSION_ENGINE = django.contrib.sessions.backends.file
必须配置SESSION_FILE_PATH
,默认使用tempfile.gettempfile()
获取临时文件目录,作为Session文件的保存目录,同时需要具有读写权限。
- 基于加密Cookie的会话
SESSION_ENGINE = django.contrib.sessions.backends.signed_cookies
Django使用加密签名工具和安全密钥设置保存会话的Cookie
Session中间件
Django的中间件框架是Django处理请求和响应的一套钩子函数的集合。传统Django视图模式是"HTTP请求 > View > HTTP响应",加入中间件框架后视图模式则变为"HTTP请求 > 中间件处理 > app > 中间件处理 > HTTP响应"。
Session操作
Session操作 | 描述 |
---|---|
request.session["key"] = "value" | 设置键名为key的会话的键值 |
request.session.setdefault("key", "value") | 设置会话键值对字典{"key":"value"} |
value = request.session["key"] | 获取会话中建名为key的键值 |
value = request.session.get("key", None) | 获取会话中键名为key的键值若不存在则默认为None |
del request.session["key", "value"] | 删除指定键值对的Cookie |
request.session.delete() | 删除当前请求下的所有Session但不删除Cookie |
request.session.flush() | 删除当前请求下所有Session同时删除Cookie |
keys = request.session.keys() | 获取Session中所有键名返回字典 |
values = request.session.values() | 获取Session中所有键值返回字典 |
items = request.session.items() | 获取所有键值对返回字典 |