【Django】第09回 cookie、session、django中间件、csrf跨站请求伪造
1. cookie与session
1.1 cookie由来
HTTP协议是无状态的。
无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。
HTTP协议四大特性
1.基于请求响应
2.基于TCP、IP作用于应用层之上协议
3.无状态
服务端无法识别客户端的状态
1.互联网刚开始兴起的的时候 所有人访问网址都是一样的数据,服务端无法识别客户端问题不大
2.互联网发展 淘宝、京东、阿里,服务端不得不想办法记住客户端的状态,cookie与session应运而生
4.无连接
什么是Cookie
保存在客户端上跟用户信息(状态)相关的数据。Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。
Cookie的原理
cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
查看Cookie
我们使用Chrome浏览器,打开开发者工具。
1.2 django操作cookie
如果想要让客户端浏览器保存cookie需要HttpResonse对象调用方法
return HttpResponse()
return render()
return redirect()
return JsonRepsonse()
设置cookie需要变形
obj = HttpResponse()
obj.操作cookie的方法
return obj
JsonResponse、render、redirect都是直接或间接的属于HttpResponse对象,所以使用方法也是一致。
obj = render()
obj.操作cookie的方法
return obj
obj = redirect()
obj.操作cookie的方法
return obj
obj = JsonRepsonse()
obj.操作cookie的方法
return obj
登录设置cookie
1.登录设置cookie
2.多个视图函数都需要校验用户是否登录(装饰器)
3.如何记住用户登录之前想要访问的页面 用户登录成功之后自动跳转
场景1:用户访问了其他需要登录才可以访问的页面 如何跳转>>>:想要访问的
场景2:用户直接访问的登录页面 如何跳转>>>:网址首页
4. 请求路径
print(request.path)
print(request.path_info)
print(request.get_full_path())
from django.shortcuts import render, HttpResponse,redirect
# Create your views here.
def set_cookie(request):
obj = HttpResponse('XXX')
# 让浏览器保存cookie数据,是以键值对的形式存储的
obj.set_cookie('name', 'jason')
return obj
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason' and password == '123':
obj = HttpResponse('登录成功')
obj.set_cookie('name', 'jasonNB')
return obj
return render(request, 'login.html')
return render(request, 'login.html')
def home(request):
if request.COOKIES.get('name'):
return HttpResponse('home 页面 只有登录的用户才可以查看')
return redirect('/login/')
def index(request):
if request.COOKIES.get('name'):
return HttpResponse('index页面 只有登录的用户才可以查看')
return redirect('/login/')
校验用户是否登陆的装饰器
from django.shortcuts import render, HttpResponse,redirect
# 校验用户是否登陆的装饰器
def login_auth(func):
def inner(request, *args, **kwargs):
# print(request.path_info)
# print(request.get_full_path()) # 两种方法能够获取到用户上一次想要访问的url
# 获取用户上一次想访问的url
target_url = request.get_full_path()
# 获取cookie
if request.COOKIES.get('username'):
return func(request, *args, **kwargs)
else:
# 在用户没有登陆的情况 携带用户想访问的url参数
return redirect('/login/?next=%s' % target_url)
return inner
# 登陆功能
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason' and password == '123':
# 获取用户上一次想要访问的url
target_url = request.GET.get('next') # 这个结果可能是None
if target_url:
# 保存用户登陆状态
obj = redirect(target_url)
else:
# 保存用户登陆状态
obj = redirect('/home/')
# 2.让浏览器记录cookie数据 设置cookie
obj.set_cookie('username','jason666') # 告诉浏览器存一个k:v键值对
"""
1.浏览器不单单会帮你存
2.而且后面每次访问你的时候还会带着它过来
"""
# 跳转到一个需要用户登陆之后才能看的页面
return obj
return render(request, 'login.html')
@login_auth
def home(request):
# # 1.获取cookie信息 判断有没有登陆
# if request.COOKIES.get('username') == 'jason666':
# return HttpResponse('home主页,登陆成功之后才能进入')
# # 2.没有登陆应该跳转到登陆页面
# return redirect('/login/')
# 装饰器代表以上nobility方式
return HttpResponse('home主页,登陆成功之后才能进入')
@login_auth
def index(request):
return HttpResponse('index页面,登陆成功之后才能进入')
@login_auth
def func(request):
return HttpResponse('func页面,登陆成功之后才能进入')
1.3 session的由来
ookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。
Session:保存在服务端上跟用户信息(状态)相关的数据
ps:session的工作需要依赖于cookie 就算是目前所有能够识别用户身份的网址也都需要使用cookie(客户端浏览器也有权拒绝保存cookie)
1.4 django操作session
设置djanjo操作session,session保存用户信息需要一张表。django_session需要提前执行数据库迁移命令。不然找不到那张表数据没法保存。
1. 请求来的时候服务端会产生随机字符串并发送给客户端
2. 服务端存储随机字符串与用户信息的对应关系
3. 客户端携带随机字符串
4. 服务端自动校验这些随机字符串
请求来之后服务端产生随机字符串并发送给客户端保存 服务端存储随机字符串与用户信息的对应关系 之后客户端携带随机字符串 服务自动校验
1.django默认的session失效时间14天
2.客户端会接收到键值对 键默认是sessionid值是加密的随机字符串(令牌)
session操作
# 获取、设置、删除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的超时时间
request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
Session流程解析
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,默认修改之后才保存(默认)
Django中Session相关设置
2. django中间件
2.1 中间件介绍
官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。
我们一直都在使用中间件,只是没有注意到而已,打开Django项目的Settings.py文件,看到下图的MIDDLEWARE配置项。
MIDDLEWARE配置项是一个列表(列表是有序的)列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
django中间件类似于django的门户 所有的请求来和响应走都必须经过中间件
django默认自带七个中间件 每个中间件都有各自负责的功能
django中间件除了默认的之外 还支持自定义中间件(无限)
django中间件使用场景
全局相关的功能:
全局用户身份校验 全局用户黑名单校验 全局用户访问频率校验
2.2 自定义中间件(五种)
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
自定义中间件的规则
1.要继承MIDDLEWAREMIXIN类,
2.要重写父类方法
3.将类添加到setting.py文件中MIDDLEWARE配置项里
自定义一个中间件示例
2.3 五种详解
2.3.1 process_request
1. 执行时间
在视图函数之前执行
2. 参数
request 和视图中的request是同一个
3. 返回值
返回None
返回response对象
不执行后面中间的process_request方法和视图
直接执行当前值中间件的process_response方法
4. 执行顺序
按照注册的顺序执行
5. 详解
请求来的时候会按照配置文件中注册了的中间件 从上往下依次执行每一个中间件里面的process_request方法 如果没有则直接跳过,不同中间件之间传递的request都是同一个对象,该方法如果返回了HttpResonse对象 那么请求不会再往后执行 原路返回
2.3.2 process_response
1. 执行时间
在视图函数之后执行
2. 参数
request 和视图中的request是同一个
response 返回的response对象
3. 返回值
返回response对象
4. 执行顺序
返回response对象
5. 详解
1.响应走的时候会按照配置文件中注册了的中间件 从下往上一次执行每一个中间件里面的process_response方法 没有没有则直接跳过
2.该方法有两个形参request和response 并且默认情况下应该返回response
3.该方法也可以自己返回HttpResponse对象 相当于狸猫换太子
ps:如果请求的过程中process_request方法直接反悔了HttpResponse对象那么会原地执行同级别process_response方法返回(flask则不同)
2.3.3 process_view
1. 执行时间
在视图函数之前,process_request之后执行
2. 参数
request 是HttpRequest对象。
view_func 将要执行的视图函数
view_args 视图函数的可变长位置参数
view_kwargs 视图函数的可变长关键字参数
3. 返回值
返回 None 正常执行
返回 response对象 不执行后面的process_view和视图,直接执行所有中间件的process_response方法
4. 执行顺序
按照注册的顺序执行
5. 详解
process_view方法是在Django路由系统之后,视图系统之前执行的,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的,如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,那么将不会执行Django的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
2.3.4 process_template_response(条件触发:视图返回的response有render方法)
1. 执行时间
在视图函数之后,process_response之前执行
2. 参数
request 是HttpRequest对象。
response 是TemplateResponse对象
3. 返回值
返回 response对象
4. 执行顺序
按照注册的倒序执行,执行完所有process_template_response方法后执行response.render方法
5. 详解
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)
2.3.5 process_exception(有条件触发:有错误才执行)
1. 执行时间
在视图函数之后,process_response之前执行
2. 参数
request 是HttpRequest对象。
exception 错误对象
3. 返回值
返回 None 不对错误进行处理,交给下一个中间件进行处理
返回 response对象 下一个中间的process_exception不执行,直接执行所有中间件的process_response方法
4. 执行顺序
按照注册的倒序执行
5. 详解
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
2.4 中间件的执行流程
请求到达中间件之后,先按照正序执行每个注册中间件的process_request方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法(注意不是掉头执行所有的process_response方法),将HttpResponse对象返回给浏览器。也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。
process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。假如中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行。
process_template_response和process_exception两个方法的触发是有条件的,执行顺序也是倒序。总结所有的执行流程如下: