1. Cookie与Session介绍
1.1 cookie
客户端与服务端交互时,存储在客户端上与用户信息相关的数据。
Cookie是存储在用户计算机上的小型文本文件,用于跟踪用户在网站上的活动。
初始版本的Cookie只能存储很少的数据,并且没有强制加密机制,容易被篡改或窃取。
1.2 Session简介
Session是在服务端存储用户会话数据的一种技术。
每当用户访问网站时,服务器会为其创建一个唯一的Session标识符(Session ID),并将会话数据存储在服务器上。
Session ID一般通过Cookie或URL参数传递给客户端,用于识别用户的会话状态。
Session ID通过Set-Cookie发送给浏览器(Cookie 名通常为JSESSIONID或PHPSESSID)。
浏览器存储 Session ID:浏览器将包含Session ID的 Cookie 保存为会话 Cookie
1.3 Cookie和Session的关联
在实际应用中,Cookie和Session通常结合使用。
当用户首次访问网站时,服务器会为其分配一个唯一的Session ID,通过Set-Cookie发送给浏览器,。
随后,客户端在每次请求中都会携带该Cookie,服务器通过解析Cookie中的Session ID,读取对应的会话数据,实现用户状态的跟踪和管理。
1.4 token
Token 身份验证是无状态的分布式身份认证方案,核心是:服务器不存储用户会话状态,所有身份信息都加密编码在客户端持有的 Token 字符串中,每次请求由服务器验证 Token 的合法性来完成身份校验。
客户端 服务器| |
1. 提交用户名/密码登录请求 -----> || || 2. 验证用户凭证| || <----- 3. 生成并返回签名Token || |
4. 本地存储Token(localStorage/sessionStorage/HttpOnly Cookie)| |
5. 后续请求携带Token(Authorization: Bearer <token>)| -----> || || 6. 验证Token签名+有效期| || <----- 7. 返回受保护资源/401未授权 |
token最主流实现:JWT(JSON Web Token)
.分隔,格式:Header.Payload.Signature| 部分 | 作用 | 内容示例 | 关键说明 |
|---|---|---|---|
| Header(头部) | 声明 Token 类型和签名算法 | {"alg":"HS256","typ":"JWT"} |
固定 Base64 编码,任何人可解码 |
| Payload(载荷) | 存储身份信息和声明 | {"sub":"123456","name":"张三","exp":1714000000} |
仅 Base64 编码,绝对不能放密码、手机号等敏感信息 |
| Signature(签名) | 防篡改核心 | HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload), secret) |
用服务器私钥 / 密钥生成,只有服务器能验证 |
签名防篡改原理:
服务器用只有自己知道的密钥,对 Header+Payload 的拼接字符串进行哈希签名
客户端修改任何一段内容,重新计算的签名都会和原签名不一致
服务器验证时,会用相同的密钥重新计算签名,对比是否匹配,不匹配直接拒绝
Token 与 Session-Cookie 核心区别
| 维度 | Token(JWT) | Session-Cookie |
|---|---|---|
| 存储位置 | 客户端(前端可控) | 服务器(Session)+ 客户端(Cookie 存 SessionID) |
| 状态 | 无状态(服务器不存会话) | 有状态(服务器需维护 Session 表) |
| 扩展性 | 极佳(分布式 / 微服务直接共享) | 差(需 Session 共享 / 粘性会话) |
| 跨域支持 | 天然支持(放在请求头) | 受同源策略限制 |
| CSRF 风险 | 低(不依赖 Cookie) | 高(Cookie 自动携带) |
| XSS 风险 | 高(存在 localStorage 易被窃取) | 低(HttpOnly Cookie 无法被 JS 读取) |
| 主动注销 | 困难(需黑名单机制) | 简单(直接删除服务器 Session) |
2. Django操作Cookie
2.1 前言
虽然cookie是服务端发送给客户端浏览器需要的保存内容
但是客户端浏览器可以选择拒绝保存
如果禁止自动保存cookie
那么只要是需要登录的网站都没办法正常登录了
2.2 视图函数返回值
正常情况下返回的是对象
return HttpResponse()
return render()
return redirect()
不直接返回对象,先用变量名指代,返回变量名
obj1 = HttpResponse()
return obj1obj2 = render()
return obj2obj3 = redirect()
return obj3
2.3 操作cookie
[1] 设置cookie
obj = HttpResponse()
obj.set_cookie(key,value)
return obj
参数:
key, 键
value='', 值
max_age=None, 超时时间,以秒为单位
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=False, https传输
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
[2] 获取cookie
request.COOKIES.get(key)
[3] 删除cookie
obj = redirect('/app01/login/')obj.delete_cookie('sign')return obj
2.4 设置与获取cookie代码案例
[1] 登录功能--服务端向客户端发送cookie
项目的urls.py
from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [path('admin/', admin.site.urls),path('app01/login/', views.login)
]
app01的views.py
def login(request):if request.method == 'POST':username = request.POST.get('username')if username == 'avril':obj = HttpResponse('登录成功')obj.set_cookie('name1', 'avril1')return objreturn render(request, 'login.html')
app01的login.html
<body>
<form action="" method="post"><p>username: <input type="text" name="username"></p><input type="submit">
</form>
</body>
首次打开浏览器时cookie为空

输入用户名avril点击提交之后

[2] 只有登录才能查看home页面
项目的urls.py
from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [path('admin/', admin.site.urls),path('app01/home/', views.home),path('app01/login/', views.login),
]
app01的views.py
def login(request):if request.method == 'POST':username = request.POST.get('username')if username == 'avril':return redirect('/app01/home/')return render(request, 'login.html')def home(request):return HttpResponse('只有登录的用户才可以查看home页面')
app01的login.html
<body>
<form action="" method="post"><p>username: <input type="text" name="username"></p><input type="submit">
</form>
</body>
打开登录页面,输入用户名点击提交,跳转到home页面


直接访问home页面(没有登录直接访问)

[3] 解决不登录直接访问的问题
app01的views.py
def login(request):if request.method == 'POST':username = request.POST.get('username')if username == 'avril':obj = redirect('/app01/home/')# 设置cookieobj.set_cookie('sign', 'abc123')return objreturn render(request, 'login.html')def home(request):# cookie正确则登录成功if request.COOKIES.get('sign') == 'abc123':return HttpResponse('用户已登录,当前是home页面')# cookie错误则跳转到登录页面return redirect('/app01/login/')
直接访问/app01/home/时,跳转到/app01/login/
访问登录页面,输入用户名点击提交


[4] 多个视图函数需要添加登录认证
当多个视图函数都需要用到登录认证时,需要用到装饰器
项目的urls.py
from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [path('admin/', admin.site.urls),path('app01/home/', views.home),path('app01/login/', views.login),path('app01/index/', views.index),path('app01/order/', views.order),
]
app01的views.py
from django.shortcuts import render, HttpResponse, redirect# Create your views here.def login(request):if request.method == 'POST':username = request.POST.get('username')if username == 'avril':obj = redirect('/app01/home/')# 设置cookieobj.set_cookie('sign', 'abc123')return objreturn render(request, 'login.html')def login_auth(func):def inner(request, *args, **kwargs):if request.COOKIES.get('sign') == 'abc123':res = func(request, *args, **kwargs)return reselse:return redirect('/app01/login/')return inner@login_auth
def home(request):return HttpResponse('用户已登录,当前是home页面')@login_auth
def index(request):return HttpResponse('用户已登录,当前是index页面')@login_auth
def order(request):return HttpResponse('用户已登录,当前是order页面')
当直接访问app01/home/、app01/index/、app01/order/时,跳转到login页面
输入用户名avril点击提交之后,首先直接跳转到home页面,此时有了cookie


然后可以访问app01/index/、app01/order/


当直接访问app01/home/、app01/index/、app01/order/时,跳转到login页面,输入用户名登录之后统一跳转到home页面,无法跳转到index、order
[5] 解决跳转到login之后无法跳转到目的URL的问题
(1)前提知识
URL举例:http://localhost:8000/app01/login/?next={target_url}
? 是 HTTP URL 标准中查询字符串(Query String)的唯一分隔符,它将 URL 拆分为两部分:
前面:http://localhost:8000/app01/login/ → 服务器要处理的资源路径(对应 Django 的 URLconf 路由)
后面:next={target_url} → 传给该资源的GET 请求参数(键值对形式)
1、通用 HTTP 层面的作用
传递额外的动态参数:多个参数用&连接,格式为?key1=value1&key2=value2
影响服务器响应:服务器解析这些参数后,可返回差异化内容(比如分页?page=2、筛选?status=active)
GET 请求专属:参数明文出现在 URL 中,长度有限制(浏览器 / 服务器通常限制 2KB 左右),不适合传敏感数据
2、Django 登录场景的作用
next={target_url} 是 Django 内置登录系统的标准约定参数,也是最常用的查询参数用法
功能:用户登录成功后,自动重定向到next指定的目标页面
? 之后的内容 不是 URL 的实际路径,仅作为查询参数(Query String)存在,用于向服务器传递额外的键值对数据。
服务器在进行路由匹配时只会解析 ? 之前的部分,? 之后的内容仅用于向该路径传递额外数据。
(2) 代码
app01的views.py
from django.shortcuts import render, HttpResponse, redirect# Create your views here.def login(request):if request.method == 'POST':username = request.POST.get('username')if username == 'avril':# 在登录认证已经获取目的URL的基础上,再获取目的URL# next内容是GET请求参数里的键值对next_path = request.GET.get('next')# 跳转到目的URL next_path,而非之前的/app01/home/obj = redirect(next_path)# 设置cookieobj.set_cookie('sign', 'abc123')return objreturn render(request, 'login.html')def login_auth(func):def inner(request, *args, **kwargs):print(request.get_full_path()) # 获取用户登录之前的目的URLtarget_url = request.get_full_path()if request.COOKIES.get('sign') == 'abc123':res = func(request, *args, **kwargs)return reselse:# 将目的URL当作参数传给登录函数return redirect(f'/app01/login/?next={target_url}')return inner@login_auth
def home(request):return HttpResponse('用户已登录,当前是home页面')@login_auth
def index(request):return HttpResponse('用户已登录,当前是index页面')@login_auth
def order(request):return HttpResponse('用户已登录,当前是order页面')
访问http://localhost:8000/app01/index/,仍然访问/app01/login/,但是增加了查询参数next

输入用户名avril点击提交,跳转到index页面而非之前的home页面

当直接访问login页面,输入用户名并点击提交时,报错


login函数加上if next_path
def login(request):if request.method == 'POST':username = request.POST.get('username')if username == 'avril':# 在登录认证已经获取目的URL的基础上,再获取目的URL# next内容是GET请求参数里的键值对next_path = request.GET.get('next')# 用户有可能直接访问登录页面if next_path:obj = redirect(next_path) # 跳转到目的URL next_path,而非之前的/app01/home/else:obj = redirect('/home/') # 直接访问登录页面之后跳转到home页面# 设置cookieobj.set_cookie('sign', 'abc123')return objreturn render(request, 'login.html')
直接访问login页面,输入用户名点击提交,跳转到指定的home页面


2.5 删除cookie代码案例
views.py中添加logout函数
@login_auth
def logout(request):obj = redirect('/app01/login/')obj.delete_cookie('sign')return obj
输入用户名avril,点击提交之后跳转到home页面,生成cookie


以上登录基础上访问logout,跳转到login页面,cookie被删除

3. django操作session
3.1 前言
session数据是保存在服务端的,给客户端返回的是一个加密后的字符串(sessionid)
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()# 会话session的key
request.session.session_key# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()# 检查会话session的key在数据库中是否存在
request.session.exists("session_key")# 删除当前会话的所有Session数据 只删客户端
request.session.delete()# 删除当前的会话数据并删除会话的Cookie。 服务端、客户端都删
request.session.flush() 这用于确保前面的会话数据不可以再次被用户的浏览器访问例如,django.contrib.auth.logout() 函数中就会调用它。# 设置会话Session和Cookie的超时时间 默认过期时间是14天
request.session.set_expiry(value)* 如果value是个整数,session会在些秒数后失效。* 如果value是个datatime或timedelta,session就会在这个时间后失效。* 如果value是0,用户关闭浏览器session就会失效。* 如果value是None,session会依赖全局session失效策略。
必须先进行orm迁移,在数据库中自动生成django_session表
否则直接访问设置session的视图函数会报错

3.1 设置Session
[1] 语法
request.session['key'] = value
[2] 代码
项目的urls.py
from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [path('admin/', admin.site.urls),path('app01/tool/', views.tool),
]
app01的views.py
def tool(request):request.session['sign1'] = 'abc666'return HttpResponse('设置session操作')
访问设置session的路由时,django_session数据库生成session_key、session_data,session_key以sessionid形式发送给客户端浏览器


3.2 获取Session
[1] 语法
request.session.get('key')
[2] 原理
从请求 Cookie 中读取 sessionid
通过 session_key 查找未过期的数据库记录
数据解码,返回 Python 字典
[2] 代码
项目的urls.py
from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [path('admin/', admin.site.urls),path('app01/tool/', views.tool),path('app01/get_sign/', views.get_sign)
]
app01的views.py
def tool(request):request.session['sign1'] = 'abc666'return HttpResponse('设置session操作')def get_sign(request):res = request.session.get('sign1')print(res)return HttpResponse('获取session操作')

获取session时,后端打印的是实际的session值,而非加密后的字符串

3.3 设置、获取多个session
app01的views.py
def tool(request):request.session['sign1'] = 'abc666'request.session['sign2'] = 'abc999'request.session['sign3'] = 'abc123'return HttpResponse('设置session操作')def get_sign(request):res = request.session.get('sign1')res2 = request.session.get('sign2')res3 = request.session.get('sign3')print(res, res2, res3)return HttpResponse('获取session操作')
先访问设置页面


再访问获取页面


结论:
给session设置多个值的时候,存在数据库中的数据仍是一条
但是在取session的时候,可以通过request.session对象获取到设置的多组键值对
3.4 session配置
django提供了5种类型的session
1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
其他公用设置项:
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
