官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每一个中间件组件都负责作一些特定的功能。html
可是因为其影响的是全局,因此须要谨慎使用,使用不当会影响性能。web
说的直白一点中间件是帮助咱们在视图函数执行以前和执行以后均可以作一些额外的操做,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。ajax
咱们一直都在使用中间件,只是没有注意到而已,打开Django项目的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', ]
MIDDLEWARE配置项是一个列表(列表是有序的,记住这一点,后面你就知道为何要强调有序二字),列表中是一个个字符串,这些字符串实际上是一个个类,也就是一个个中间件。django
所以:只要之后用django开发业务 设计到全局相关的功能 就考虑用中间件来完成,好比下面这些flask
全局用户身份校验浏览器
全局用户访问频率校验安全
用户访问黑名单cookie
用户访问白名单session
自定义中间件咱们须要去看一眼源码,找一下共同点
注意:找哪个中间件的源码,咱们须要将其导出来才能看到,好比SessionMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
共同点:
class SessionMiddleware(MiddlewareMixin): def process_request(self, request): def process_response(self, request, response): class CsrfViewMiddleware(MiddlewareMixin): def process_request(self, request): def process_view(self, request, callback, callback_args, callback_kwargs): def process_response(self, request, response): class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request):
django运行用户自定义中间件而且暴露给用户五个能够自定义的方法
process_request(self, request): 请求过来的时候会按照配置文件中中间件从上到下的顺序依次执行每个中间件中的process_request方法,若是没有直接跳到下一个 若在定义中间件的process_request直接返回一个HttpResponse之类的则直接跳过剩下的部分从当前中间件的porcess_response执行后直接返回 注意:Django是同级别直接返回,而flask是遇到这种状况会执行全部的process_response process_response 响应走的时候会按照配置文件中注册的中间件从下往上的顺序依次执行每个中间件里面的 process_response方法 该方法必需要有两个形参 而且须要将形参response返回 若是你内部本身返回了HttpResponse对象 会将返回给用户浏览器的内容替换成你本身的
示例:
新建一个自定义中间件文件夹,在这个文件夹下新建一个自定义中间件的py文件
from django.utils.deprecation import MiddlewareMixin class Mymid1(MiddlewareMixin): def process_request(self, request): print('我是第一个自定义中间件的process_request') 如有同级别的返回则直接跳过剩下的部分直接返回 # return HttpResponse('一个返回值') def process_response(self, request, response): print('我是第一个自定义中间件的process_response') return response class Mymid2(MiddlewareMixin): def process_request(self, request): print('我是第二个自定义中间件的process_request') def process_response(self, request, response): print('我是第二个自定义中间件的process_response') return response
而后在配置文件中的MIDDLEWARE中将自定义的中间件路径注册上
结果:
# process_view方法 def process_view(self, request, view_func, view_args, view_kwargs): print('我是第一个中间件里面的process_view')
该方法有四个参数
request是HttpRequest对象。 view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称做为字符串。) view_args是将传递给视图的位置参数的列表. view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
它应该返回None或一个HttpResponse对象。 若是返回None,Django将继续处理这个请求,
执行任何其余中间件的process_view方法,而后在执行相应的视图。 若是它返回一个HttpResponse对象,
那么将不会执行Django的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
总结:Django会在调用视图函数以前调用process_view方法。
def process_exception(self,request,exception): print('exception:',exception) print('我是第一个中间件里面的process_exception') 该方法两个参数: 一个HttpRequest对象 一个exception是视图函数异常产生的Exception对象。 这个方法只有在视图函数中出现异常了才执行,
它返回的值能够是一个None也能够是一个HttpResponse对象。
若是是HttpResponse对象,Django将调用模板和中间件中的process_response方法,
并返回给浏览器,不然将默认处理异常。若是返回一个None,则交给下一个中间件的process_exception方法来
总结:这个方法只有在视图函数中出现异常了才执行
# 自定义中间件方法 def process_template_response(self,request,response): print('我是第一个中间件里面的process_template_reponse方法') return response
# views def index(request): print('我是视图函数index') def render(): return HttpResponse("你好啊 我是index里面的render函数") obj = HttpResponse("index") obj.render = render return obj
总结:process_template_response(self, request, response) 它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。 process_template_response是在视图函数执行完成后当即执行,可是它有一个前提条件,那就是视图函数返回的对象有一个render属性对应的render方法(或者代表该对象是一个TemplateResponse对象或等价方法)。
了解一下基本的钓鱼网站
钓鱼网站 本质搭建一个跟正常网站如出一辙的页面 用户在该页面上完成转帐功能 转帐的请求确实是朝着正常网站的服务端提交 惟一不一样的在于收款帐户人不一样 给用户书写form表单 对方帐户的input没有name属性 你本身悄悄提早写好了一个具备默认的而且是隐藏的具备name属性的input
模拟真实网站和钓鱼网站
先将django配置文件里面的'django.middleware.csrf.CsrfViewMiddleware'
注释了
# 真实网站 # 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('money') print('%s 给 %s 转了 %s元' %(username, target_user, money)) return render(request, 'transfer.html') # transfer.html <h2>我是真实的网站</h2> <form action="" method="post"> <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text" name="target_user"></p> <p>money<input type="text" name="money"></p> <input type="submit"> </form> # 钓鱼网站 # 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("money") print('%s 给 %s 转了 %s元'%(username,target_user,money)) return render(request,'transfer.html') # transfer.html <h2>我是钓鱼网站</h2> # 将传输数据的网站改成真实网站网址 <form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text"></p> # 在转帐人下面新建一个隐藏的input框,默认值为jason,这样上面这个框就失效了 <input type="text" name="target_user" style="display: none" value="jason"> <p>money:<input type="text" name="money"></p> <input type="submit"> </form>
这样以上述代码就能实现他人用钓鱼网站来非法获取你的转帐信息并加以修改,那么如何改变这种状况
# 只要在上面的form表单中加上下面这个{% csrf_token %},就能够启动django配置中的django.middleware.csrf.CsrfViewMiddleware中间件,这样每次访问服务端都会随机产生一个字符串,用于识别身份(注意是没有绝对安全的web的) {% csrf_token %}
form表单经过csrf校验
{% csrf_token %}
ajax经过csrf校验
# 写一个ajax请求 <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'jason'}, success:function (data) { alert(data) } }) }) </script>
第一种方法:手动获取
<script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()}, success:function (data) { alert(data) } }) }) </script>
第二种方法:利用模板语法(本地写小项目推荐)
<script> $('d1').click(fnction(){ $.ajax({ url:'', type:'post', data:{'username':'jason','csrfmiddlewaretoken':''{{csrf_token}}''}, success:function(data){ alert(data) } }) }) </script>
第三种方法:写一个静态配置js文件引用过来(官网提供的,推荐使用,重点是知道复制第三步里面的js文件内容)
#第一步,配置静态文件 设置一个static文件夹 #第二步,在settings文件中配置静态文件路径 STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'satic') ] # 第三步,在static文件夹下新建一个配置js文件,内容以下 function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); #第四步,在ajax请求中引入这个js文件 {% load static %} <script src="{% static 'myset.js' %}"></script> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'jason'}, success:function (data) { alert(data) } }) }) </script>
FBV设置csrf装饰器
导入模块
from django.views.decorators.csrf import csrf_exempt, csrf_protect
当咱们网站总体都校验csrf的时候 我想让某几个视图函数不校验
# 在指定视图函数上面添加下面这个装饰器 @csrf_exempt
当咱们网站总体都不校验csrf的时候 我想让某几个视图函数校验
# 能够将中间件 'django.middleware.csrf.CsrfViewMiddleware',注释掉 # 而后在指定视图函数上面添加下面这个装饰器 @csrf_protect
如何将CBV设置csrf装饰器
导入模块
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator # 不管是本身写的装饰器仍是别人写好的装饰器都要记得用这个method_decorator方法
回忆一下CBV如何写的
from django.views import View class MyHome(View): def get(self,request): return HttpResponse('get') def post(self,request): return HttpResponse('post') 注意在urls.py文件中的写法 url(r'^home/',views.MyHome.as_view()),
如何让某个方法添加上csrf功能
@method_decorator(csrf_protect, name=post) # 第一种方法:指定某个方法名添加csrf class MyHome(View): def get(self,request): return HttpResponse('get') @method_decorator(csrf_protect) #第二种方法:直接在方法名上添加装饰器 def post(self,request): return HttpResponse('post')
from django.views import View from django.utils.decorators import method_decorator @method_decorator(csrf_protect,name='dispatch') # 第三种方法这样写也能够 class MyHome(View): @method_decorator(csrf_protect) # 第三种方法 类中全部的方法都装 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')
补充注意:上面的方法都是添加csrf校验,对于如何指定某个CBV中方法不校验,上面的一二两种方法都不行,只能经过第三种方法给dispatch装,将里面的csrf_protect改成csrf_exempt便可
直接进行数据库迁移命令咱们能够看到数据库中自行建立的一些表格
建立超级用户
第一步:点击快捷方式
在控制台输入命令
createsuperuser
建立成功的样子
django后台管理界面
auth模块注册功能
# 第一步导入auth模块和auth模块中的User表 from django.contrib import auth from django.contrib.auth.models import User def register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 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') # 这种建立超级用户的方式须要填入邮箱,不然报错 return render(request,'register.html')
auth模块其余功能
# 登录认证 def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 明文 # 数据库校验用户名和密码是否正确,这样是用明文校验是不对的 # User.objects.filter(username=username,password=password) # 经过auth的内置方法来校验 user_obj = auth.authenticate(request,username=username,password=password) print(user_obj) """ 用户名密码正确返回的是用户对象 错误返回None """ print(res.username) print(res.password) if user_obj: # 保存用户登陆状态 # request.session auth.login(request,user_obj) """ 只要执行了这一句话 以后在任意能够获取到request对象的地方 均可以经过request.user获取到当前登陆的用户对象 """ return HttpResponse("登陆成功") return render(request,'login.html') def get_user(request): print(request.user) """ 用户登陆成功以后 request.user拿到的就是用户对象 没有登陆 获取到的匿名用户 """ print(request.user.is_authenticated()) # 返回的是布尔值 return HttpResponse("get_user") # 局部配置校验用户是否登陆装饰器 # 先导入模块 from django.contrib.auth.decorators import login_required # 若里面不传指定login_url参数就会导入到django内置的的登录界面 #@login_required @login_required(login_url='/login/') # 这样能够跳转到自定义的登录界面 def xxx(request): return HttpResponse('xxx页面') # @login_required @login_required(login_url='/login/') def yyy(request): return HttpResponse('yyy页面') #@login_required @login_required(login_url='/login/') def zzz(request): return HttpResponse('zzz页面') # 如何设置全局配置校验用户是否登陆装饰器 # 在settings.py文件中写入下面这个代码 LOGIN_URL = '/login/' # 而后在指定视图函数上只要写下面这个就能够看,不须要在指定url了 @login_required # 若同时有局部和全局配置,优先使用局部的 #如何进行密码修改 # 首先必定是登录状态 @login_required def set_password(request): if request.method == 'POST': old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') # 1 先校验旧密码是否正确 is_right = request.user.check_password(old_password) # print(is_right) 是布尔值 # 2 再去修改新密码 if is_right: request.user.set_password(new_password) request.user.save() # 必定要save一下 不然数据库无影响 return render(request,'set_password.html') # 如何注销登录状态 @login_required def logout(request): auth.logout(request) return HttpResponse('注销成功')
如何将默认设置的user表进行扩展
# 第一种方法能够利用一对一表关系,就是新建一张表,与其是一对一关系,则两张表就是一张表,实现了扩展字段功能 #第二种,类的继承 # 在models.py文件中导入下面模块(实际上原先的django默认设置的表都是来源于AbstracterUser,所以咱们也能够继承这个类来建立本身的表) from django.contrib.auth.models import User,AbstractUser # Create your models here. class Userinfo(AbstractUser): phone = models.BigIntegerField() avatar = models.FileField() # 扩展的字段 尽可能不要与原先表中的字段冲突 # 而后在配置文件写上下面这个代码 AUTH_USER_MODEL = 'app01.Userinfo' # AUTH_USER_MODEL = '应用名.表名' """ django就会将userinfo表来替换auth_user表 而且以前auth模块全部的功能不变 参照的也是userinfo表 """