# utils/login_auth
from functools import wraps
from django.shortcuts import redirect
def login_auth(func):
@wraps(func)
def inner(request, *args, **kwargs):
target_url = request.get_full_path() # 获取用户想要访问的url
if request.COOKIES.get('is_login'):
res = func(request, *args, **kwargs)
return res
else:
return redirect(f'/login/?next={target_url}') # 设置登录后跳转的页面url
return inner
下面是视图函数:
# views.py
from utils.login_auth import login_auth
# 登录页面
def login(request):
info = {'msg':''}
# 获取用户上一次想要访问的页面
tag_url = request.GET.get('next')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'python' and password == '123':
if tag_url:
obj = redirect(tag_url)
else:
# 不能直接返回redirect,因为直接返回是无法保存cookie的,必须使用obj返回,才能记录状态,浏览器客户端才会保存
obj = redirect('/home/')
# max_age就是设置cookie的有效期,单位是秒,当达到了有效期后,浏览器客户端会自动删除本地的cookie信息
obj.set_cookie('is_login', 'this_user_has_been_logined', max_age=10000)
return obj
else:
info['msg'] = '帐号密码错误'
return render(request,'login.html',locals())
# 退出登录
@login_auth
def logout(request):
response_obj = redirect('login')
response_obj.delete_cookie('is_login')
return response_obj
# 主页
@auth
def home(request):
return HttpResponse('我是home页面')
session
Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取。因此就需要有一种新的东西,它能支持更多的字节,并且保存在服务器,有较高的安全性。这就是Session。
问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。
我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。
django中操作session
django中session的操作通过request对象下的session完成,它类似一个字典结构,操作方式和字典的操作非常类似。session中的数据是保存在服务端的数据库django_session
表中,给客户端返回的是一个随机字符串,django默认session的过期时间是14天,可以认为修改它。django_session表中的数据条数是取决于浏览器的,同一个计算机上(IP地址)同一个浏览器只会有一条数据生效。主要是为了节省服务端数据库资源。
# 获取、设置、删除Session中数据
request.session['k1'] # 拿到'k1'对应的value
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 # 随机字符串ryl3jza580roxfntx8wittr0vykvmi5h
# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
# 检查会话session的key在数据库中是否存在
request.session.exists("session_key")
# 删除当前会话的所有Session数据
request.session.delete() # 只删服务端的 客户端的不删
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内部发生的事情
当使用request.session['is_login'] = 'login'
时会发生下述事情:
1.django内部会自动帮你生成一个随机字符串
2.django内部自动将随机字符串和对应的数据存储到django_session表中
2.1先在内存中产生操作数据的缓存
2.2在响应结果django中间件的时候才真正的操作数据库
2.1先在内存中产生操作数据的缓存
2.2在响应结果django中间件的时候才真正的操作数据库
django_session表内存储格式
session_key session_data expire_date
随机字符串1 对应数据密文 过期时间
随机字符串2 对应数据密文 过期时间
......
3.将产生的随机字符串返回给客户端浏览器保存
用画图来来表示过程如下:
使用request.get['is_login']
时发生的事情:
1.自动从浏览器请求中获取sessionid对应的随机字符串
2.拿着该随机字符串去django_session表中查找对应的数据
3.如果比对上了 则将对应的数据取出并以字典的形式封装到request.session中
如果比对不上 则request.session.get()返回的是None
CBV添加装饰器
实现登陆之后才能访问其他页面的需求在使用FBV的情况下,只需要为视图函数添加装饰器即可,那么CBV模式的视图类又如何实现此需求呢?这里介绍三种添加装饰器的方式,在这之前需要明确一点,CBV中django不建议直接给类的方法添加装饰器,不论装饰器是否生效。
CBV遇到需要加装饰器时,需要先导入from django.utils.decorators import method_decorator
,在然后在指定方法上加@method_decorator(method_name)
,如果加在类上,还需要提供装饰的名字:@method_decorator(decorator_name, method_name)
,下面是添加装饰器的方式:
给方法加装饰器
from django.views import View
from django.utils.decorators import method_decorator
class MyLogin(View):
@method_decorator(login_auth) # 方式1:给get方法添加装饰器
def get(self,request):
return HttpResponse("get请求")
def post(self,request):
return HttpResponse('post请求')
# 优点:简单直接
# 缺点:多个方法时,需要多次添加装饰器
给类加装饰器
from django.views import View
from django.utils.decorators import method_decorator
@method_decorator(login_auth, name='get') # 同时提供装饰器和被装饰的方法
@method_decorator(login_auth, name='post') # 支持装饰器多个叠加
class MyLogin(View):
def get(self,request):
return HttpResponse("get请求")
def post(self,request):
return HttpResponse('post请求')
# 优点:给不同的方法,添加不同的装饰器
通过dispatch方法
from django.views import View
from django.utils.decorators import method_decorator
class MyLogin(View):
@method_decorator(login_auth) # 方式3:它会直接作用于当前类里面的所有的方法
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self,request):
return HttpResponse("get请求")
def post(self,request):
return HttpResponse('post请求')
# 优点:一次性给所有方法添加装饰器(某些特殊装饰器只能加在dispatch上,例如csrf认证)
# 缺点:灵活性差