Day59 Django中间件,csrf跨站请求伪造,auth模块,settings插拔式配置

一.Django中间件

1.什么是中间件

Django的中间件相似因而Django的保安html

  请求来的时候须要先通过中间件才能到达Django的后端前端

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

官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每一个中间件组件都负责作一些特定的功能。ajax

可是因为其影响的是全局,因此须要谨慎使用,使用不当会影响性能。数据库

说的直白一点中间件是帮助咱们在视图函数执行以前和执行以后均可以作一些额外的操做,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。django

中间件在Django中就位于settings.py文件中的MIDDLEWARE配置项后端

# 下面这几行代码就是Django的中间件
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配置项是一个列表(列表是有序的,记住这一点,后面你就知道为何要强调有序二字),列表中是一个个字符串,这些字符串实际上是一个个类,也就是一个个中间件。浏览器

咱们以前已经接触过一个csrf相关的中间件了?咱们一开始让你们把他注释掉,再提交post请求的时候,就不会被forbidden了,咱们学会使用csrf_token以后就再也不注释这个中间件了。cookie

 

 

Django中间件能够用来作什么session

1.网站全集的身份校验,访问频率限制,权限校验,涉及到全局的校验均可以在中间件中完成

2.Django的中间件是全部web框架中作的最好的

 

2.自定义中间件

中间件能够定义五个方法,分别是:(主要的是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)

以上方法的返回值能够是None或一个HttpResponse对象,若是是None,则继续按照django定义的规则向后继续执行,若是是HttpResponse对象,则直接将该对象返回给用户。

 

如何自定义咱们本身的中间件

  1.若是想让本身写的中间件生效,就必须先继承MiddlewareMixin

  2.在注册自定义中间件的时候,必定要确保路径不能写错

须要掌握的两个方法

process_request(self,request)方法和process_response(self,request,response)方法

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class MyMdd(MiddlewareMixin):
    def process_request(self,request):
        print('我是第一个中间件里的process_request方法')
    
    def process_response(self,request,response):
        print('我是第一个中间件里的process_response方法')
        return response

class MyMdd1(MiddlewareMixin):
    def process_request(self,request):
        print('我是第二个中间件里的process_request方法')
        return HttpResponse('第二个中间件就返回了')

    def process_response(self,request,response):
        print('我是第一个中间件里的process_response方法')
        return response

class MyMdd2(MiddlewareMixin):
    def process_request(self,request):
        print('我是第三个中间件里的process_request方法')

    def process_response(self,request,response):
        print('我是第一个中间件里的process_response方法')
        return response
下方的代码

process_request

  1.请求来的时候,会通过每一个中间件里面的process_request方法,从上往下

  2.若是方法里返回了HTTPResponse对象,那么会直接返回,再也不往下执行

  基于该特色就能够作访问频率限制,身份校验,权限校验等

class MyMdd(MiddlewareMixin):
    def process_request(self,request):
        print('我是第一个中间件里的process_request方法')

class MyMdd1(MiddlewareMixin):
    def process_request(self,request):
        print('我是第二个中间件里的process_request方法')

class MyMdd2(MiddlewareMixin):
    def process_request(self,request):
        print('我是第三个中间件里的process_request方法')

 

当在某个process_request方法直接返回了HttpResponse对象就不会继续执行了

# 第一个中间件就返回了
class MyMdd(MiddlewareMixin):
    def process_request(self,request):
        print('我是第一个中间件里的process_request方法')
        return HttpResponse('第一个中间件就返回了')

 

 

 

process_response

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

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

# process_response方法
class MyMdd(MiddlewareMixin):
    def process_request(self,request):
        print('我是第一个中间件里的process_request方法')

    def process_response(self,request,response):
        print('我是第一个中间件里的process_response方法')
        return response

class MyMdd1(MiddlewareMixin):
    def process_request(self,request):
        print('我是第二个中间件里的process_request方法')

    def process_response(self,request,response):
        print('我是第二个中间件里的process_response方法')
        return response

class MyMdd2(MiddlewareMixin):
    def process_request(self,request):
        print('我是第三个中间件里的process_request方法')

    def process_response(self,request,response):
        print('我是第三个中间件里的process_response方法')
        return response

 

 

 

须要了解的三个方法

process_view()

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

class MyMdd(MiddlewareMixin):
    def process_view(self,request,view_func,view_args,view_kwargs):
        print(view_func)
        print(view_args)
        print(view_kwargs)
        print('我是第一个中间件里的process_view方法')

...

view_func会拿到视图函数名

 

 

 

process_exception()

  当视图函数报错时,自动执行

class MyMdd(MiddlewareMixin):
    def process_exception(self,request,exception):
        print('我是第一个中间件里的process_exception方法')

...

 

 

 

process_template_response()

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

# 视图函数层
def index(request):
    print('我是index函数')
    def render():
        return HttpResponse('包含render属性')
    obj = HttpResponse('index')
    obj.render = render
    return obj
class MyMdd(MiddlewareMixin):
    def process_template_response(self,request,response):
        print('我是第一个中间件里的process_template_response方法')
        return response

...

总结:在书写中间件的时候,只要形参中有response,就须要将其返回,这个response就是给前端的响应信息

 

二.csrf跨站请求伪造

伪造的钓鱼网站

钓鱼网站

  经过制做一个跟正儿八经的网站如出一辙的页面,骗取用户输入信息 转帐交易,从而作手脚,转帐交易的请求确确实实是发给了中国银行,帐户的钱也是确确实实少了,惟一不同的地方在于收款人帐户不对

 

内部原理

  在让用户输入对方帐户的那个input上面作手脚,给这个input不设置name属性,在内部隐藏一个实现写好的name和value属性的input框,这个value的值 就是钓鱼网站受益人帐号

<h1>这是真的网站</h1>
<form action="" method="post">
    <p>本人:<input type="text" name="username"></p>
    <p>对方帐户:<input type="text" name="target_username"></p>
    <p>金额:<input type="text" name="price"></p>
    <input type="submit">
</form>
真实页面前端页面
<h1>这是钓鱼网站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>本人:<input type="text" name="username"></p>
    <p>对方帐户:<input type="text"></p>
    <p><input type="hidden" name="target_username" value="sxc"></p>
    <p>金额:<input type="text" name="price"></p>
    <input type="submit">
</form>
钓鱼网站前端

 

如何防止钓鱼网站

防止钓鱼网站的思路

  真实网站经过返回给用户的form表单页面偷偷的塞一个随机字符串

  当请求到来的时候,会先比对随机字符串是否一致,若是不一致,直接返回403页面

该随机字符串有如下特色

  1.同一个浏览器每次访问都不同

  2.不一样浏览器绝对不会重复

 

避免csrf校验

1.form表单发送post请求的时候,须要在form表单里加上一句话

{% csrf_token %}

这时候csrf中间件须要打开才能正确的验证

 'django.middleware.csrf.CsrfViewMiddleware',

 

2.ajax发送post请求,避免csrf校验

一共有三种方式

  1.先在页面上写{% csrf_token %}利用标签查找,获取到该input标签的键值信息

// 第一种方式
            data:{'username':'sxc','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},

  2.直接书写'{{ csrf_token }}'

// 第二种方式
            data:{'username':'sxc','csrfmiddlewaretoken':'{{ csrf_token }}'},

  3.导入js文件,将获取随机键值对的方法写到该js文件中

首先新建一个js文件,存放如下代码,以后导入该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);
    }
  }
});

在html页面直接引入该js文件便可

{% load static %}
<script src="{% static 'setjs.js' %}"></script>

 

跨站请求伪造相关装饰器

当针对FBV时

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

from django.views.decorators.csrf import csrf_exempt,csrf_protect

# 导入模块后直接装饰在函数前
@csrf_exempt
def login(request):
    return HttpResponse('login')

 

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

# 装饰在函数前
@csrf_protect
def lll(request):
    return HttpResponse('lll')

 

当针对CBV时

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

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')

总结:csrf装饰器中只有crsf_exempt是特例,其余的装饰器在给CBV装饰时均可以有三种方式

 

三.Auth登陆认证模块

1 Auth模块是什么

Auth模块是Django自带的用户认证模块:

咱们在开发一个网站的时候,无可避免的须要设计实现网站的用户系统。此时咱们须要实现包括用户注册、用户登陆、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。

Django做为一个完美主义者的终极框架,固然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。

 

2 auth模块经常使用方法

导入模块

from django.contrib import auth

 

auth模块的功能

查询用户
    from django.contrib import auth
    user_obj = auth.authenticate(username=username,password=password)  # 必需要用 由于数据库中的密码字段是密文的 而你获取的用户输入的是明文
记录用户状态
    auth.login(request,user_obj)  # 将用户状态记录到session中
判断用户是否登陆
    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/'
验证密码是否正确
    request.user.check_password(old_password)
修改密码    
    request.user.set_password(new_password)
    request.user.save()  # 修改密码的时候 必定要save保存 不然没法生效
退出登录
    auth.logout(request)  # request.session.flush()
注册用户
        # 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')  # 建立超级用户  邮箱必填

 

3 扩展默认的auth_user表

这内置的认证系统这么好用,可是auth_user表字段都是固定的那几个,我在项目中无法拿来直接使用啊!

好比,我想要加一个存储用户手机号的字段,怎么办?

聪明的你可能会想到新建另一张表而后经过一对一和内置的auth_user表关联,这样虽然能知足要求可是有没有更好的实现方式呢?

答案是固然有了。

咱们能够经过继承内置的 AbstractUser 类,来定义一个本身的Model类。

这样既能根据项目需求灵活的设计用户表,又能使用Django强大的认证系统了。

复制代码
复制代码
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
    """
    用户信息表
    """
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=11, null=True, unique=True)
    
    def __str__(self):
        return self.username
复制代码
复制代码

注意:

按上面的方式扩展了内置的auth_user表以后,必定要在settings.py中告诉Django,我如今使用我新定义的UserInfo表来作用户认证。写法以下:

# 引用Django自带的User表,继承使用时须要设置
AUTH_USER_MODEL = "app名.UserInfo"

再次注意:

一旦咱们指定了新的认证系统所使用的表,咱们就须要从新在数据库中建立该表,而不能继续使用原来默认的auth_user表了。

 

自定义auth_user表

from django.contrib.auth.models import AbstractUser
# Create your models here.
# 第一种 使用一对一关系  不考虑


# 第二种方式   使用类的继承
class Userinfo(AbstractUser):
    # 千万不要跟原来表中的字段重复 只能创新
    phone = models.BigIntegerField()
    avatar = models.CharField(max_length=32)

# 必定要在配置文件中 告诉django
# 告诉django  orm再也不使用auth默认的表  而是使用你自定义的表
AUTH_USER_MODEL = 'app01.Userinfo'  # '应用名.类名'


1.执行数据库迁移命令
全部的auth模块功能 所有都基于你建立的表 
而再也不使用auth_user

 

三.settings功能插拔式源码原理借鉴

参考Django配置文件中间件的功能模块

  一旦注释  >>>  该功能即不能用
  一旦打开  >>>  该功能即启用

咱们首先写三个功能

class  Msg(object):
    def __init__(self):
        pass  # 发送短信须要的代码配置

    def send(self,content):
        print('短信通知:%s' % content)
msg.py
class Email(object):
    def __init__(self):
        pass  # 发送邮件须要的代码配置

    def send(self,content):
        print('邮件通知:%s'%content)
email.py
class QQ(object):
    def __init__(self):
        pass  # 发送qq须要的代码准备

    def send(self,content):
        print('qq通知:%s'%content)
qq.py

将这三个py文件放在一个文件夹中,这样这个文件夹就成为了一个包

咱们在配置文件中配置好三个功能的路径

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

在包中的__init__文件中书写关键的代码

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)  # 对象调方法

关键代码解析:

  1.从settings中循环拿出每一个功能的路径

  2.解压赋值,分红路径和类名

  3.使用importlib经过字符串导入路径

  4.利用反射,经过类名字符串获取类

  5.实例化生成对象并调用对象的方法

 

在启动文件中传入参数就可使用这三个功能

import notify

notify.send_all('国庆放假了 记住放八天哦')
start.py
相关文章
相关标签/搜索