cookie与session、FBV与CBV如何添加装饰器、django中间件
cookie与session
HTTP协议四大特性
1.基于请求响应
2.基于TCP、IP作用于应用层之上协议
3.无状态
服务端无法识别客户端的状态
1.互联网刚开始兴起的的时候 所有人访问网址都是一样的数据
服务端无法识别客户端问题不大
2.互联网发展 淘宝、京东、阿里
服务端不得不想办法记住客户端的状态
cookie与session应运而生
4.无连接
我们都知道HTTP协议是无状态的,无状态的意思就是每次请求都是独立的,它的执行情况和结果与前面的请求和之后都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况;
'''人生只如初见''',对服务器来说,每次请求都是全新的;
状态可以理解为客户端和服务端在某次会话中产生的数据,那无状态的就以为这些数据不会被保留,会话中产生的数据又是我们需要保存的,也就是说要'保持状态',因此cookie就是在这样一个场景下诞生。
1.什么是cookie(保存在客户端上跟用户信息(状态)相关的数据)
Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息
2.cookie的原理
由服务器产生内容,浏览器收到请求后保存在本地,当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是谁了
3.cookie主要用于以下三个方面
1.会话状态管理
2.个性化设置
3.浏览器行为跟踪
4.什么是session(保存在服务端上跟用户信息(状态)相关的数据)
Session代表着服务器和客户端一次会话的过程。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者超时Session超时失效时会话结束。
5.cookie与session有什么不同
作用范围不同,Cookie保存在客户端(浏览器),Session保存在服务器端。
存取方式的不同,Cookie只能保存ASCII,Session可以存任意数据类型,一般情况下我们可以在Session中保持一些常用变量信息,比如说Userid等。
有效期不同,Cookie可设置为长时间保持,比如我们经常使用的默认登陆功能,Session一般失效时间较短,客户端关闭或者Session超时都会失效
隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。
session是基于cookie工作的(其实大部分的保存用户状态的操作都需要使用到cookie)
cookie 操作
虽然cookie是服务端告诉客户端浏览器需要保存内容
但是客户端浏览器可以选择拒绝保存,如果禁止了,那么只要需要记录用户状态的网站登陆功能都无法使用了!
如果想要让客户端浏览器保存cookie需要HttpResonse对象调用方法
直接返回 HttpResonse
return HttpResonse
间接返回 HttpResonse
return render()
return rediect()
return JsonResponse()
# 先用一个变量去接收HttpResponse()
obj = HttpResonse()
...(obj.操作cookie的方法)
return obj
设置cookie需要变形
obj = HttpResponse()
obj.操作cookie的方法
return obj
obj = render()
obj.操作cookie的方法
return obj
obj = redirect()
obj.操作cookie的方法
return obj
obj = JsonRepsonse()
obj.操作cookie的方法
return obj
登录操作
清楚数据
没有登录直接访问home页面
跳转到了登陆页面
from django.shortcuts import render, HttpResponse,redirect
# Create your views here.
def set_cookie(request):
obj = HttpResponse('迟到的深情比草贱!')
# 让浏览器保存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页面,登陆成功之后才能进入')
如何记住用户登录之前想要访问的页面 用户登录成功之后自动跳转
可以拿到用户想访问的后缀,那么问题又来了,如果用户像访问一个没有登录的页面,那么它先走哪一步呢>>>登录装饰器,那么就可以在登录装饰器上操作数据
def login_auth(func_name):
def inner(request, *args, **kwargs):
if request.COOKIES.get('name'):
res = func_name(request, *args, **kwargs)
return res
else:
target_path = request.path_info
return redirect(f'/login/?next={target_path}') # ?后面的不参与路由匹配
return inner
如果是login页面那么后面就不会带参数了
场景1:用户访问了其他需要登录才可以访问的页面 如何跳转>>>:想要访问的
在装饰器login_auth内设置一个获取用户上一次想访问的url,将用户想访问的url放在当作参数get请求重定向发送给login,login获取用户想访问的url,然后判断生成对象,重定向返回
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason' and password == '123':
target_path = request.GET.get('next')
if target_path:
obj = redirect(target_path) # 如果有值则跳转到指定页面
else:
obj = redirect('/home/') # 如果没有值则跳转到首页
obj.set_cookie('name', 'jasonNB')
return obj
return render(request, 'login.html')
场景2:用户直接访问的登录页面 如何跳转>>>:网址首页
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason' and password == '123':
target_path = request.GET.get('next')
if target_path:
obj = redirect(target_path) # 如果有值则跳转到指定页面
else:
obj = redirect('/home/') # 如果没有值则跳转到首页
obj.set_cookie('name', 'jasonNB', max_age=3)
return obj
return render(request, 'login.html')
设置过期时间
例如:如果我们登录了一个网站长时间没有浏览,那么过一段时间它就会要求重新登录
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason' and password == '123':
target_path = request.GET.get('next')
if target_path:
obj = redirect(target_path) # 如果有值则跳转到指定页面
else:
obj = redirect('/home/') # 如果没有值则跳转到首页
obj.set_cookie('name', 'jasonNB', max_age=3) # 设置3秒,3秒过后就会登录失效
return obj
return render(request, 'login.html')
注销功能
注销之后,就已经清除Cookie,就不能再访问home了
# 只有登陆的用户才能注销
@login_auth
def logout(request):
# 返回主页
obj = redirect('/login/')
# 删除指定的键值对
obj.delete_cookie('username')
return obj
session 操作
session的工作机制
当客户端登录成功之后,然后服务端会产生一个随机字符串返回给客户端保存,并且服务端争对这些随机字符串会对用户数据做对应关系,然后做一个保存,将这些数据保存到服务端,然后存到表中,这里报这个错误是因为django在执行数据库迁移命令的时候,会自动创建好多好多张不是我们添加进去的表,这些表中就有django_session表,这张表就是专门用来存储session相关服务端的数据,因此想要执行session,必须先执行迁移命令
1.django默认的session失效时间14天
2.客户端会接收到键值对 键默认是sessionid值是加密的随机字符串(令牌)
执行迁移命令以后,就可以访问到了
request.session['name'] = 'jason'
1.django自动产生一个随机字符串返回给客户端(对name加密)
2.往django_session创建数据(对jason加密)
def set_session(request):
request.session['name'] = 'jason'
return HttpResponse('设置session')
request.session.get('name')
1.自动从请求中回去sessionid对应的随机字符串
2.拿着随机字符串去django_session中匹配数据
3.如果匹配上还会自动解密数据并展示
def get_session(request):
print(request.session.get('name'))
return HttpResponse('获取session')
session流程解析
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,默认修改之后才保存(默认)
Django中Session相关设置
django中间件
django中间件类似于django的门户,所有的请求和响应走都必须经过中间件,django默认自带七个中间件,每一个中间件都有它自己的功能,除了它默认的之外,还支持自定义中间件,加多少个都可以
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中间件使用场景
全局相关的功能:
全局用户身份校验,全局用户黑名单校验 全局用户访问频率校验
中间件是可以拿到ip地址的,关于黑名单校验,它会进行判断,这个ip地址的身份,如果是黑名单用户,那么就直接不让他进入后面,直接pass掉它了,那么关于访问频率的校验,先设置多长时间内访问多少次,然后中间件进行识别计算看你出现过几次
django自定义中间件可以有五个方法
django自定义中间件可以有五个方法,每个方法都会在特定的场景下触发
它是由以下五个
process_request
process_response
process_view(了解)
process_template_response(了解)
process_exception(了解)
定义中间件
如果想要写一些第三方的插件功能,可以在应用下创建一个文件,在这个文件下写一些py文件
1.定义中间件
from django.utils.deprecation import MiddlewareMixin
class MyMdd1(MiddlewareMixin):
def prcess_request(self, request):
print('MyMdd1 process_request')
class MyMdd2(MiddlewareMixin):
def prcess_request(self, request):
print('MyMdd2 process_request')
2.配置中间件
1.process_request
1.请求来的时候会按照配置文件中注册了的中间件 从上往下依次执行每一个中间件里面的process_request方法 如果没有则直接跳过
2.该方法如果返回了HttpResonse对象 那么请求不会再往后执行 原路返回
2.process_response
1.响应走的时候会按照配置文件中注册了的中间件 从下往上一次执行每一个中间件里面的process_response方法 没有没有则直接跳过
2.该方法有两个形参request和response 并且默认情况下应该返回response
3.该方法也可以自己返回HttpResponse对象 相当于狸猫换太子
ps:如果请求的过程中process_request方法直接反悔了HttpResponse对象那么会原地执行同级别process_response方法返回(flask则不同)
3.process_view
当路由匹配成功之后 执行视图函数之前 自动触发
process_view(self, request, view_func, view_args, view_kwargs)
该方法有四个参数
request是HttpRequest对象
view_func是Django即将使用的视图函数。(它是实际的函数对象,而不是函数的名称作为字符串)
view_args是将传递给视图的位置参数的列表.
view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
Django会在调用视图函数之前调用process_view方法。
它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,那么将不会执行Django的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
4.process_excption
当视图函数报错之后 自动触发
process_exception(self, request, exception)
该方法两个参数:
一个HttpRequest对象
一个exception是视图函数异常产生的Exception对象。
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行
5.process_template_response
当视图函数返回的数据对象中含有render属性对应render函数才会触发
process_template_response(self, request, response)
它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。