django框架之中间件 Auth模块
CBV加装饰器
方式一:装饰器加到想装饰的方法上
方式二:装饰器加到class前面,通过name参数指定被装饰的方法
方式三:重写dispatch(django分发CBV视图函数),直接给dispatch装饰,该类中所有对象方法都将被装饰
from django.shortcuts import render, HttpResponse, redirect from django.views import View from functools import wraps from django.utils.decorators import method_decorator def login(request): if request.method == 'POST': name = request.POST.get('name') pwd = request.POST.get('pwd') if name == 'Tom' and pwd == '123': request.session['name'] = 'Tom' return redirect('/home/') return render(request, 'login.html') def login_auth(func): @wraps(func) def inner(request, *args, **kwargs): if request.session.get('name'): return func(request, *args, **kwargs) return redirect('/login/') return inner # @method_decorator(login_auth, name='get') # 第二种,name参数必须指定 class MyHome(View): @method_decorator(login_auth) # 第三种,拦截django分发视图函数的过程 def dispatch(self, request, *args, **kwargs): super().dispatch(request, *args, **kwargs)
# @method_decorator(login_auth) # 第一种 def get(self, request): return HttpResponse('get') def post(self, request): return HttpResponse('post')
中间件
处理Django的请求和响应的框架级别的钩子,相当于django的门户;
用于在全局范围内改变Django的输入和输出的插件系统,
本质上就是一个自定义类,Django框架会在请求的特定的时间去执行这些方法。
django中间件可以实现网站全局相关的功能校验:身份验证(RBAC基于角色的权限管理),黑白名单、访问频率限制、反爬相关....
中间件在settings.py的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默认7个中间件,给用户提供五个自定义中间件的方法:
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)process_request 请求时触发,从上往下依次执行,没有该方法则直接通过 ******
返回值None:继续往后执行;
返回HttpResponse对象:将从同级别process_response返回该对象给浏览器
2)process_view 路由匹配成功,执行视图函数之前触发,从上往下依次执行
返回值None:继续往后执行;
返回HttpResponse对象:将从下往上依次执行每个process_response,返回该对象给浏览器
3)process_exception 视图函数报错时自动触发,从下往上依次执行
4)process_template_response 视图函数返回的对象有一个render()方法时触发(或表明该对象是TemplateResponse对象或等价方法)
从下往上依次执行,中间报错无返回函数,不会执行
5)process_response 响应时触发,从下往上依次执行 ******
必须将response形参接收的数据返回,不然直接报错
自定义中间件
1. 新建一个文件夹,放入自定义中间件.py文件
2. 导模块导入MiddlewareMixin
3. 自定义继承MiddlewareMixin的类(中间件)
4. 将自定义中间件路径在settings.py文件中的 MIDDLEWARE列表 注册
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class MyMiddleWare(MiddlewareMixin): def process_request(self, request): print('process_request 自定义No.1 方法') # return HttpResponse('haha') def process_response(self, request, response): print('process_response 自定义No.1 方法') return response def process_view(self, request, view_func, view_args, view_kwargs): print('process_view 自定义No.1 方法') print(view_func.__name__, view_func) return HttpResponse('**************') def process_exception(self, request, exception): print('process_exception 自定义No.1 方法') print(exception) def process_template_response(self, request, response): print('process_template_response 自定义No.1 方法') return response class MyMiddleWare1(MiddlewareMixin): def process_request(self, request): print('process_request 自定义 No.2 方法') # return HttpResponse('xixi') def process_response(self, request, response): print('process_response 自定义No.2 方法') return response def process_view(self, request, view_func, view_args, view_kwargs): print('process_view 自定义No.2 方法') print(view_func.__name__, view_func) def process_exception(self, request, exception): print('process_exception 自定义No.2 方法') print(exception) def process_template_response(self, request, response): print('process_template_response 自定义No.2 方法') return response
from django.shortcuts import HttpResponse def index0(request): print('我是视图函数index') # jhgyuy return HttpResponse('index') def index(request): print('我是视图函数index') def render(): print("in index/render") return HttpResponse("564K") rep = HttpResponse("OK") rep.render = render # jkgjlie return rep
'app01.mymiddle.ware.MyMiddleWare', 'app01.mymiddle.ware.MyMiddleWare1',
Csrf 跨站请求伪造
模拟钓鱼网站过程
def transfer(request): if request.method == 'POST': username = request.POST.get('username') money = request.POST.get('money') others = request.POST.get('others') print('%s 给%s 转了¥%s' % (username, others, money)) return render(request, 'transfer.html')
<h1>正常网站</h1> <form action="" method="post"> <p>username:<input type="text" name="username"></p> <p>money:<input type="text" name="money"></p> <p>others:<input type="text" name="others"></p> <input type="submit"> </form>
<h1>钓鱼网站</h1> <form action="" method="post"> <p>username:<input type="text" name="username"></p> <p>money:<input type="text" name="money"></p> <p>others:<input type="text"></p> <input type="text" name="others" value="Tom" style="display:none"> <input type="text"> </form>
钓鱼网站和正常网站页面完全一样(路径有别),在钓鱼网站中,第三个others参数另一方账户无法正常输入,
但提交的数据最终提交到正常网站,操作成功,但转账将将转到别的账户,这样,网站存在很大的风险.......
django框架通过中间件 'django.middleware.csrf.CsrfViewMiddleware',可以动态生成一条随机数据,每刷新一次页面重新生成,将该数据附在提交数据中,若该数据不存在,或者和该网站内部的数据无法匹配上,即拒绝执行后续操作。如何在post请求中携带该数据?可将该该数据隐藏于一个标签:
1. form表单跨站请求伪造:
{% csrf_token %}
2. Ajax跨站请求伪造,data中加入该键值对:
'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()
or
'csrfmiddlewaretoken':'{{ csrf_token }}'
页面数据:<input type="hidden" name="csrfmiddlewaretoken" value="CWg1rnImJfBiJrdVV6iGORUQQHrIAkWJn3nU3VdFnJmNXx8FTac1UZk2eIsQa6Cz">
<h1>正常网站</h1> <form action="" method="post"> {% csrf_token %} <p>username:<input type="text" name="username"></p> <p>money:<input type="text" name="money"></p> <p>others:<input type="text" name="others"></p> <input type="submit"> </form>
{% csrf_token %} <button>ajax</button> <script> $('button').click(function () { $.ajax({ url:'', type:'post', data:{'name': 'jason','csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()}, succecc:function (data) { console.log(data) } }) }) </script>
单独配置csrf校验
全局校验csrf时,某个视图函数不需要校验;全局不校验时,某个视图函数需要校验?
1.FBV的csrf校验
from django.views.decorators.csrf import csrf_exempt, csrf_protect @csrf_exempt # 不校验csrf def exempt(request): return HttpResponse('exempt') @csrf_protect # 校验csrf def protect(request): return HttpResponse('protect')
2. CBV的csrf校验
csrf_protect 校验:和正常的CBV装饰器一样,三种
# @method_decorator(csrf_protect, name='post') # 第一种 class index2(View): @method_decorator(csrf_protect) def dispatch(self, request, *args, **kwargs): # 第三种 super().dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('get') # @method_decorator(csrf_protect) # 第二种 def post(self, request): return HttpResponse('post')
csrf_exempt校验:不能单独给一个方法加校验,只能配置到一个路由下,两种
@method_decorator(csrf_exempt, name='dispatch') # 第一种 class index1(View): # @method_decorator(csrf_exempt) # 第二种 def dispatch(self, request, *args, **kwargs): super().dispatch(request, *args, **kwargs) def get(self, request): return HttpResponse('get') def post(self, request): return HttpResponse('post')
Auth模块
django自带的用户功能模块
常用语句:
auth.authenticate(request, **kwargs) 查询用户
.login(request, user_obj) 记录用户登录状态,任何地方request.user获取登录对象
.logout(request) 退出登录
User.objects.create(**kwargs) 新建用户,密码是明文
.create_user(**kwargs) 新建用户,普通用户
.create_superuser(**kwargs) 新建vip,必须有Email,具有Django后台管理登录权限
命令:python3 manage.py createsuperuser 创建超级用户:vip
request.user 获取当前登录对象
.is_authenticated() 是否登录
.password 查询密码
.check_password('num') 核对密码是否正确
.set_password('num') 修改密码
.save() 保存修改
登录
from django.contrib import auth def auth_login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # models.User.objects.filter(username=username, password=password).first() user_obj = auth.authenticate(request, username=username, password=password) # 查看是否存在 if user_obj: # 记录用户状态 request.session['name'] = 'jason' auth.login(request, user_obj) # 生成记录后,通过request.user获取到当前登录对象 return HttpResponse('ok') return render(request, 'auth_login.html')
def auth_index(request): print(request.user.is_authenticated()) # 判断当前用户是否登录 print(request.user, type(request.user)) # 获取当前登录用户对象 return HttpResponse('index') def auth_logout(request): auth.logout(request) # request.session.flush() 退出登录 return HttpResponse('ok')
注册
from django.contrib.auth.models import User def auth_register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user_obj = auth.authenticate(request, username=username) # 查看是否存在 if user_obj: return HttpResponse('当前用户已存在!') # User.objects.create(username=username, password=password) # 密码是明文 # User.objects.create_user(username=username, password=password) # 普通用户 User.objects.create_superuser(username=username, password=password, email='123@qq.com') # vip,必须有Email return render(request, 'auth_register.html')
修改密码
def auth_password(request): print(request.user.password) # 拿到密码(密文) is_true = request.user.check_password('123') # 核对密码 if is_true: request.user.set_password('321') # 修改密码 request.user.save() # 保存修改 return HttpResponse('change ok')
登录验证
auth模块提供登录验证功能,需导入login_required
from django.contrib.auth.decorators import login_required # 局部登录验证 @login_required(login_url='/auth_login/') # 默认跳转登录'/login/',路径不同则需指定 def auth_home(request): return HttpResponse('必须登录才能访问')
所有视图函数都需要登录验证,可以在settings.py设置全局登录参数配置
# auth 没有登录,则自动跳转登录页面 LOGIN_URL = '/auth_login/'
Auth模块自定义表
1. 导入模块中的数据:User, AbstractUser
2. 在settings.py中配置好参数,使用自定义的表名
AUTH_USER_MODEL = 'app名.models对应的模型表名'
3. 在使用Auth模块的的常用语句时,使用自定义类名即可
models.py
from django.db import models from django.contrib.auth.models import User, AbstractUser class UserInfo(models.Model): # 第一种:一对一关联表(了解) phone = models.CharField(max_length=32) avatar = models.CharField(max_length=32) ab = models.OneToOneField(to=User) class Userinfo(AbstractUser): # 第二种:面向对象的继承AbstractUser phone = models.CharField(max_length=32) avatar = models.CharField(max_length=32)
settings.py
# 告诉django不在使用默认的auth_user,使用自定义的表 AUTH_USER_MODEL = 'app01.Userinfo'