Django中间件-跨站请求伪造-django请求生命周期-Auth模块-seettings实现可插拔配置(设计思想)

Django中间件

1、什么是中间件

  django中间件就是相似于django的保安;请求来的时候须要先通过中间件,才能到达django后端(url,views,models,templates),css

响应走的的时候也须要通过中间件才能到达web服务器网关接口处;html

中间件位于web服务端与url路由层之间;是介于request与response处理之间的一道处理过程。前端

2、中间件有什么用

  若是你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些均可以经过中间件来实现。python

能够用来作什么?

  一、网站全局的身份校验,访问频率限制,权限检验等;只要涉及到全局校验的均可以用中间件来实现jquery

  二、Django的中间件是全部的web框架中作得最好的web

Django默认的中间件:(在django项目的settings模块中,有一个MIDDLEWARE_CLASSES变量,其中的每个元素就是一个中间件)ajax

django默认的中间件有七个以下图:

 3、自定义中间件

  中间件能够定义五个方法;其中主要的是(process_request:请求 和process_response:返回)数据库

一、process_request(self,request)

二、process_view(self, request, callback, callback_args, callback_kwargs)

三、process_template_response(self,request,response)

四、process_exception(self, request, exception)

五、process_response(self, request, response)

  以上的方法的返回值能够是None或一个HttpResponse对象,若是是None,继续按照Django定义的规则向后继续执行,django

若是是HttpResponse对象,则直接将该对象返回给用户便可。编程

一、process_request和process_response

  当用户发起请求的时候会依次通过全部的的中间件,这个时候的请求时process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。

以下图所示,一个完整的中间件的流程:

二、Djang请求生命周期:

wsgi---中间件---路由---视图---中间件---wsgi--

  经过完整Django的完整构造图,可以扩展结合Django每一个知识点,深刻了解每一个知识点所涉及到的内容。

三、须要重点掌握的中间件方法:

  一、.process_request()方法

    规律:

      一、请求来的时候,会通过每一个中间件里面的process_request()方法(从上到下的顺序)

      二、若是返回的是HttpResponse对象,那么会直接返回,再也不往下执行了;基于这一特色就能够作访问的频率限制,身份校验,权限校验等

  二、process_response()方法

   规律:

    (1)、必须将response形参返回,由于这个形参指代的就是要返回给前端的数据。

    (2)、响应走的时候,会依次通过每个中间件里面的process_response方法(从下往上)

须要了解的方法:

    (1)、process_view() :

            在路由匹配成功执行视图函数以前 触发

    (2)、process_exception() :

            当你的视图函数报错时  就会自动执行

    (3)、process_template_response() 

            当你返回的HttpResponse对象中必须包含render属性才会触发

 四、自定义的中间件,写的类必须继承 MiddlewareMixin

 (1)第一步:导入

from django.utils.deprecation import MiddlewareMixin

 (2)、自定义中间件,新建文件件书写

from django.utils.deprecation import MiddlewareMixin#
from django.shortcuts import HttpResponse
#
class Md1(MiddlewareMixin):
#
    def process_request(self,request):
        print("Md1请求")
 #
    def process_response(self,request,response):
        print("Md1返回")
        return response
#
class Md2(MiddlewareMixin):
#
    def process_request(self,request):
        print("Md2请求")
        #return HttpResponse("Md2中断")
    def process_response(self,request,response):#
        print("Md2返回")
        return response

 (3):在views中定义一个视图视图函数(index)

def index(request):

    print("view函数...")
    return HttpResponse("OK")

(4)、在settings.py的MIDDLEWARE里注册本身定义的中间件

1.若是你想让你写的中间件生效,就必需要先继承MiddlewareMixin
2.在注册自定义中间件的时候,必定要确保路径不要写错

(5)查看运行的结果:得出上面的总结规律

请求得出的规律:

返回得出的规律:

第二种状况,当自定义的中间件种有HttpRsponse时,直接返回:

(6)若是没有返回response形参,由于这个形参指代的就是要返回给前端的数据

 报错结果显示:

 (7)、其余方法了解:

一、process_view

  该方法有四个参数

process_view(self, request, view_func, view_args, view_kwargs)

实例:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,redirect,render,reverse

class Md1(MiddlewareMixin):
    def process_request(self,request):
        print('Md1请求')
        return HttpResponse("Md1中断")

    def process_response(self,request,response):
        print('Md1返回')
        return response
        # return HttpResponse("嘿嘿!")

    def process_view(self,request,callback,callback_args,callback_kwargs):
        print('Md1views')

class Md2(MiddlewareMixin):
    def process_request(self,request):
        print("Md2请求")
        return HttpResponse('Md2中断')

    def process_response(self,request,response):
        print('Md2返回')
        return response

    def process_view(self,callback,callback_args,callback_kwargs):
        print("Md2views")

二、process_exception,该方法两个参数:

process_exception(self, request, exception)

 

一个HttpRequest对象

一个exception是视图函数异常产生的Exception对象。

三、process_template_response(self,request,response)方法:

  该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法

总结:你在书写中间件的时候 只要形参中有repsonse 你就顺手将其返回 这个reponse就是要给前端的消息

 2、CSRF_TOKEN跨站请求伪造

  一、什么是csrf

  CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding;

简单的理解:就是攻击者盗用了你的身份,以你的名义发送恶意的请求,对服务器来讲这个请求是彻底合法的;

要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  1.登陆受信任网站A,并在本地生成Cookie。
  2.在不登出A的状况下,访问危险网站B。

简单的举例钓鱼网站:开两个django项目,模拟转帐的现象

正规的网站:

views.py

def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        money = request.POST.get('money')
        target_user = request.POST.get('target_user')
        print('%s 给 %s 转了 %s元'%(username,target_user,money))
    return render(request,'res.html')

res.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<h2>这是正儿八经的网站</h2>
<form action="//" method="post">
{#    {% csrf_token %}#}
    <p>本人用户名:<input type="text" name="username"></p>
    <p>转帐金额:<input type="text" name="money"></p>
    <p>对方帐户:<input type="text" name="target_user"></p>
    <input type="submit">
</form>

钓鱼网站破解原理:

  在让用户输入对方帐户的那个input上面作手脚,写一个同样的viewx.py,和路由url,

修改前端HTML的内容让转帐对方的用户隐藏起来绑定value=’jason‘,启动时修改端口。

 防止钓鱼网站的思路:

  网站会给返回的用户的form表单页面,偷偷的噻一个随机的字符串,请求来的时候,

会先比对随机字符串是否一致,若是不一致,直接拒绝(403)

该随机字符串有一下特色:

  一、同一个浏览器没一次访问都不同

  二、不一样的浏览器之间绝对不会重复

跨站请求伪造的解决方法:

一、form表发送post请求的时候,只须要书写一句话便可

  {% csrf_token %}

书写{% csrf_token %},会在客户端生成一对键值对

二、用AJAX发送post请求时,如何避免csrf校验

  (1)、如今页面上写{% csrf_token %},利用标签查找 ,获取到该input键值信息,关键字:'csrfmiddlewaretoken'

{'username':'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}

 

   (2)、直接书写'{{ csrf_token }}'

{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}

  (3)、你能够将该获取随机键值对的方法,写到一个js文件中,以后只须要导入该文件便可使用。

添加static到settings.Py中:

 而后在使用的前端html页面导入:

书写静态文件存放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);
    }
  }
});

 3、跨站请求伪造相关的装饰器

    1.当你网站全局都须要校验csrf的时候 有几个不须要校验该如何处理?
     2.当你网站全局不校验csrf的时候 有几个须要校验又该如何处理?

全站禁用:注释掉中间件 'django.middleware.csrf.CsrfViewMiddleware',

局部禁用:用装饰器(在FBV中使用)

 在CBV中使用:

   CBV比较特殊,不能单独加在某个方法上;只能加在类上或dispatch方法上

from django.test import TestCase

# Create your tests here.
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt, csrf_protect

# 这两个装饰器在给CBV装饰的时候 有必定的区别
若是是csrf_protect
那么有三种方式

# 第一种方式
# @method_decorator(csrf_protect,name='post')  # 有效的
class MyView(View):
    # 第二种方式
    # @method_decorator(csrf_protect)
    def dispatch(self, request, *args, **kwargs):
        res = super().dispatch(request, *args, **kwargs)
        return res

    def get(self, request):
        return HttpResponse('get')

    # 第三种方式
    # @method_decorator(csrf_protect)  # 有效的
    def post(self, request):
        return HttpResponse('post')

若是是csrf_exempt
只有两种(只能给dispatch装)
特例

@method_decorator(csrf_exempt, name='dispatch')  # 第二种能够不校验的方式
class MyView(View):
    # @method_decorator(csrf_exempt)  # 第一种能够不校验的方式
    def dispatch(self, request, *args, **kwargs):
        res = super().dispatch(request, *args, **kwargs)
        return res

    def get(self, request):
        return HttpResponse('get')

    def post(self, request):
        return HttpResponse('post')

Auth认证模块

  执行数据库迁移的那两条命令时,即便咱们没有建表,django是否是也会建立好多张表?

咱们建立以后去看一下里面的一个叫auth_user表,这个表跟用户的相关,既然是表,那确定应该有对应的操做改表的方法

 auth跟用户相关的功能模块:用户的注册、登陆、验证、修改密码等。

 首先建立超级用户:createsuperuser,这个超级用户就能够拥有登录django admin后台管理的权限

在建立超级用户时:不可手动插入,由于密码事加密的

这个超级用户能够登陆到Django后台管理权限 :

 

 

基于这张表写一个登陆的功能:

  若是想用auth模块,那就必须用全套,好比用户的获取和保存等auth.authenticate,后期就不能用session来保存

from django.contrib import auth # 必需要用 由于数据库中的密码字段是密文的 而你获取的用户输入的是明文
查询用户,比较数据
user_obj = auth.authenticate(username=username,password=password)
记录用户状态 auth.login(request,user_obj)
# 将用户状态记录到session中 判断用户是否登陆,用了auth。login 后就能够用.属性获取 print(request.user.is_authenticated) # 判断用户是否登陆,若是是大家用户会返回False 用户登陆以后 获取用户对象 print(request.user) # 若是没有执行auth.login那么拿到的是匿名用户 校验用户是否登陆 from django.contrib.auth.decorators import login_required @login_required(login_url='/xxx/') # 局部配置 def index(request): pass

修改为全局配置 放在settings文件中,LOGIN URL = "/XXX/"

 

方法在用户注册登陆的简单校验的实际运用:

from django.contrib import auth
def xxx(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(username=username,password=password) print(user_obj)
        # print(user_obj)
        # print(user_obj.username)
        # print(user_obj.password)
        # 保存用户状态
        # request.session['user'] = user_obj
        auth.login(request,user_obj)  # 将用户状态记录到session中
        """只要执行了这一句话  你就能够在后端任意位置经过request.user获取到当前用户对象"""
    return render(request,'xxx.html')

def yyy(request):
    print(request.user)  # 若是没有执行auth.login那么拿到的是匿名用户
    print(request.user.is_authenticated)  # 判断用户是否登陆  若是是大家用户会返回False
    # print(request.user.username)
    # print(request.user.password)
    return HttpResponse('yyy')

修改密码、退出登陆、注册用户等功能

装饰器校验是否登录及跳转

from django.contrib.auth.decorators import login_required

@login_required(login_url='/login/',redirect_field_name='old') 
# 没登录会跳转到login页面,而且后面会拼接上你上一次想访问的页面路径/login/?next=/test/,能够经过参数修改next键名 def my_view(request): pass

 

若是我全部的视图函数都须要装饰并跳转到login页面,那么我须要写好多份

# 能够在配置文件中指定auth校验登录不合法统一跳转到某个路径
LOGIN_URL = '/login/'  # 既能够局部配置,也能够全局配置

回到最上面,咱们是怎么对auth_user表添加数据的?命令行输入~~~合理不?

from django.contrib.auth.models import User
def register(request):
  User.objects.create()  # 不能用这个,由于密码是明文
  User.objects.createuser()  # 建立普通用户
  User.objects.createsuperuser()  # 建立超级用户

 

校验密码,修改密码

request.user.check_password(pwd)  # 为何不直接获取查,由于前端用户输入的是明文数据库密文

request.user.set_password(pwd)
request.user.save()  # 修改密码

自带的登陆装饰器:

from django.contrib.auth.decorators import  login_required

 具体的使用

from django.contrib.auth.decorators import  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')
        # 先判断原密码是否正确
        # 将获取的用户密码 自动加密 而后去数据库中对比当前用户的密码是否一致
        is_right = request.user.check_password(old_password) 
        if is_right:
            print(is_right)
            # 修改密码
 request.user.set_password(new_password)
            request.user.save()  # 修改密码的时候 必定要save保存 不然没法生效
    return render(request,'set_password.html')

注销用户: @login_required(/login/url='xxx'/)
def logout(request): # request.session.flush() auth.logout(request) 退出 return HttpResponse("logout") 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_obj = User.objects.filter(username=username) if not user_obj: # User.objects.create(username =username,password=password) # 建立用户名的时候 千万不要再使用create 了 # 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功能

  如何扩张一张表auth_user表呢?

一对一的关联(不推荐使用)

from django.contrib.auth.model import User

class UserDetail(models.Models):
  phone = models.CharField(max_length=11)
  user = models.OnoToOneField(to=User)

面向对象的继承

from django.contrib.auth.models import User,AbstractUser
class UserInfo(AbstractUser):
  phone = models.CharField(max_length=32)

# 须要在配置文件中,指定我再也不使用默认的auth_user表而是使用我本身建立的Userinfo表
AUTH_USER_MODEL = "app名.models里面对应的模型表名"

"""
自定义认证系统默认使用的数据表以后,咱们就能够像使用默认的auth_user表那样使用咱们的UserInfo表了。
库里面也没有auth_user表了,原来auth表的操做方法,如今所有用自定义的表都可实现
"""

 

使用的userinfo表须要在settings中配置,告诉Django不在自动创user表了,换成user info表

之后在使用时,建立的用户表能够添加除了auth_user自带的字段外,只须要添加额外的字段名。

新建立的django的user表都会自带如下一些字段:

如如下BBS项目中models.py中user表的建立

 数据库迁移命令执行后会额外的添加字段:

 

 基于Django 中间件思想,实现可插拔功能

  简单的说就是,当在settings中把中间件某些相关的功能注释掉以后,某些功能就会失效,打开又可使用

按照settings源码结构分析,推导得出相应的结论,基于这结论之上,写一些自定义的中间件:

举例开发一个实现集体通信发信息,如用短信、微信、QQ发

分析:

  根据不一样功能写不一样的文件,秉承python的编成思想用鸭子类型,面向对象式编程写成类定义相同的def方法,

而后再继承一样的send_all,再建立一个settings把全部单独的功能添加配置。

新建一个文件:包含三个方法:email、msg、wechat __init__

email.py

class Email(object):
    def __init__(self):
        pass
    def send(self,content):
        print('邮件通知:%s'%content)

msg.py

class Msg(object):
    def __init__(self):
        pass
    def send(self,content):
        print('短信通知:%s'%content)

wechat.py

class WeChat(object):
    def __init__(self):
        pass
    def send(self,content):
        print('微信通知:%s'%content)

核心部分的代码 __init__.py

import settings
import importlib

def send_all(content):
    for path_str in settings.NOTIFY_LIST:  # 1.拿出一个个的字符串   'notify.email.Email'
        module_path,class_name = path_str.rsplit('.',maxsplit=1)  # 2.从右边开始 按照点切一个 ['notify.email','Email']
        module = importlib.import_module(module_path)  # from notity import msg,email,wechat
        cls = getattr(module,class_name)  # 利用反射 一切皆对象的思想 从文件中获取属性或者方法 cls = 一个个的类名
        obj = cls()  # 类实例化生成对象
        obj.send(content)  # 对象调方法

settings.py

NOTIFY_LIST = [
    'notify.email.Email',
    'notify.msg.Msg',
    'notify.wechat.WeChat',
    # 'notify.qq.QQ',
]

start.py

import  notify

notify.send_all('国庆放假了,学习使我快乐!')

把QQ的注释掉执行的的结果以下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

  

 

 

 

 

---恢复内容结束---

相关文章
相关标签/搜索