中间件可以定义五个方法,分别是:(主要的是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.在项目名或者应用名下创建一个任意名称的文件夹
2.在该文件夹内创建一个任意名称的py文件
3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin),然后在这个类里面就可以自定义五个方法了
4.需要将类的路径以字符串的形式注册到配置文件(MIDDLEWARE)中才能生效
process_request(self,request)
1.请求来的时候需要经过每一个中间件里面的process_request方法,结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
2.如果中间件里面没有定义该方法,那么直接跳过,执行下一个中间件
3.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行而是直接原路返回
process_request方法就是用来做全局相关的所有限制功能
必须掌握的两个方法:
process_response
1.响应走的时候需要结果每一个中间件里面的process_response方法,该方法有两个额外的参数request,response
2.该方法必须返回一个HttpResponse对象,默认返回的就是形参response也可以自己返回自己的
3.顺序是按照配置文件中注册了的中间件从下往上依次经过,如果没有定义,直接跳过执行下一个
研究如果第一个process_request方法就是已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况?
是其他情况,就是会直接走同级别的process_response返回
了解即可:
process_view
路由匹配成功之后执行视图函数之前,会自动执行中间件里面的该方法,顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
process_template_response
返回的HttpResponse对象有render属性的时候才会触发,顺序是按照配置文件中注册了的中间件从下往上依次经过
process_exception
当视图函数中出现异常的情况下触发,顺序是按照配置文件中注册了的中间件从下往上依次经过
from django.utils.deprecation import MiddlewareMixin
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
print("第一个自定义中间件里面的process_request方法")
def process_response(self, request, response):
print('我的第一个自定义中间件里面的process_response方法')
return response
def process_view(self, request, view_name, *args, **kwargs):
print(view_name, args, kwargs)
print("我是第一个自定义中间件里面的process_view")
def process_template_response(self, request, response):
print("我是第一个自定义中间件里面的process_template_response")
return response
def process_exception(self, request, exception):
print("我是第一个中间件里面的process_exception")
print(exception)
# urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 转账
path('transfer/', views.transfer),
]
# views.py
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_user = request.POST.get('target_user')
money = request.POST.get('monet')
print(f'{username}给{target_user}转了{money}元')
return render(request, 'transfer.html')
正经网站
# urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('transfer/', views.transfer),
]
# views.py
def transfer(request):
if request.method == 'POST':
return redirect('http://127.0.0.1:8000/transfer/')
return render(request, 'transfer.html')
钓鱼网站
如何规避上述问题:
csrf跨站请求伪造校验
网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识
当这个页面朝后端发送post请求的时候,我们后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)如果成功则正常执行
# form表单如何符合校验
# ajax
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.utils.decorators import method_decorator
"""
csrf_protect:需要校验
针对csrf_protect符号之前的装饰器的三种方法
csrf_exempt:忽视校验
针对csrf_protect只能给dispatch方法加才有效
"""
# @csrf_exempt
@csrf_protect
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_user = request.POST.get('target_user')
money = request.POST.get('monet')
print(f'{username}给{target_user}转了{money}元')
return render(request, 'transfer.html')
from django.views import View
# @method_decorator(csrf_protect, name='post') # 针对csrf_protect 第二种方式可以
# @method_decorator(csrf_exempt, name='post') # 针对csrf_exempt 第二种方式不可以
class MyCsrfToken(View):
# @method_decorator(csrf_protect) # 针对csrf_protect 第三种方式可以
@method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以
def dispatch(self, request, *args, **kwargs):
return super(MyCsrfToken, self).dispatch(request, *args, **kwargs)
def get(self, request):
return HttpResponse('get')
# @method_decorator(csrf_protect) # 针对csrf_protect 第一种方式可以
# @method_decorator(csrf_exempt) # 针对csrf_exempt 第一种方式不可以
def post(self, request):
return HttpResponse('post')
Auth模块是Django自带的用户认证模块:
我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。
createsuperuser:创建管理员用户
from django.contrib import auth
authenticate() 验证用户名以及密码是否正确,正确则返回一个 User 对象,否则None
用法:user_obj = auth.authenticate(request, username=username, password=password)
login() 保存用户状态
用法:auth.login(request, user_obj) # 类似于request.session[key] = user_obj
auth.logout(request) 当调用该函数时,当前请求的session信息会全部清除
用法:auth.logout(request) # 类型于request.session.flush()
is_authenticated 判断用户是否登录
用法:print(request.user.is_authenticated)
login_requierd() auth提供的一个装饰器工具,用来快捷的给某个视图添加登录校验。
用法:
from django.contrib.auth.decorators import login_required
@login_required # 全局配置
# 全局配置需要到settings.py文件中配置
# 全局配置:没有登录跳转到指定的页面
LOGIN_URL = '/login/' """
@login_required(login_url='/login/') # 局部配置:用户没有登录跳转到login_user后面指定的网址
create_user() 创建普通用户
用法:User.objects.create_user(username=username, password=password)
create_superuser() 创建管理员用户
用法:User.objects.create_superuser(username=username, email='[email protected]', password=password)
check_password(password) auth 提供的一个检查密码是否正确的方法,需要提供当前请求用户的密码,密码正确返回True,否则返回False。
用法:is_right = request.user.check_password(old_password)
set_password(new_password) auth 提供的一个修改密码的方法,新密码作为参数
用法:
request.user.set_password(new_password) # 修改对象属性
request.user.save() # 保存到数据库
# settings.py
# 全局配置:没有登录跳转到指定的页面
LOGIN_URL = '/login/'
# urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 登录功能
path('login/', views.login),
# 校验用户是否登录
path('home/', views.home),
path('index/', views.index),
# 修改密码
path('set_password', views.set_password),
# 注销功能
path('logout/', views.logout),
# 注册功能
path('register/', views.register),
]
# views.py
from django.shortcuts import render, HttpResponse, redirect
from django.contrib import auth
# Create your views here.
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 去用户表中校验数据
user_obj = auth.authenticate(request, username=username, password=password)
# print(user_obj)
"""
1.自动查找auth_user表
2.自动给密码加密再比对
该方法的注意事项:括号内必须同时传入用户名和密码,不能只传用户名
"""
# 判断用户是否存在
if user_obj:
# 保存用户状态
auth.login(request, user_obj) # 类似于request.session[key] = user_obj
# 最要执行了该方法,就可以在任何地方通过request.user获取到当前登录的用户对象
return redirect('/home/')
return render(request, 'login.html')
from django.contrib.auth.decorators import login_required
"""
# 全局配置需要到settings.py文件中配置
# 全局配置:没有登录跳转到指定的页面
LOGIN_URL = '/login/'
"""
# @login_required(login_url='/login/') # 局部配置:用户没有登录跳转到login_user后面指定的网址
@login_required # 全局配置
def home(request):
"""用户登录之后才能看"""
print(request.user) # 用户对象 AnonymousUser匿名用户
# 自动去django_session里面查找对应的用户对象给你封装到request.user中
# 判断用户是否登录
print(request.user.is_authenticated)
return HttpResponse("hom页面")
@login_required # 优先级局部大于全局
def index(request):
return HttpResponse("index页面")
@login_required
def set_password(request):
if request.method == 'POST':
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
confirm_password = request.POST.get('confirm_password')
# 先校验两次密码是否一致
if new_password == confirm_password:
# 校验老密码对不对
is_right = request.user.check_password(old_password)
if is_right:
# 修改密码
request.user.set_password(new_password) # 修改对象属性
request.user.save() # 保存到数据库
return render(request, 'set_password.html', locals())
@login_required
def logout(request):
auth.logout(request) # 类型于request.session.flush()
return redirect('/login/')
from django.contrib.auth.models import User
def register(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 查找auth_user表写入数据
# User.objects.create(username=username, password=password) # 写入数据不能用create 密码没有加密处理
# 创建普通用户
# User.objects.create_user(username=username, password=password)
# 创建管理员用户:
User.objects.create_superuser(username=username, email='[email protected]', password=password)
return render(request, 'register.html')
# login.html
# set_password.html
# register.html
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
"""
前提:
1.在继承之前没有执行过数据库迁移命令
auth_user没有被创建,如果当前数据库已经创建了那么就重新换一个库
2.继承的类里面不要覆盖AbstractUser里面的字段
表里面的字段都不要动,只拓展额外的字段即可
3.需要在配置文件中告诉django你要用UserInfo替换auth_user
AUTH_USER_MODEL = "app名.UserInfo"
"""
phone = models.CharField(max_length=11, null=True, unique=True)
def __str__(self):
return self.username