Django中间件

Django中间件

详情参见:http://liuqingzheng.top/python/Django%E6%A1%86%E6%9E%B6/16-Django%E9%AB%98%E7%BA%A7%E4%B9%8B-%E4%B8%AD%E9%97%B4%E4%BB%B6/

"""
django中间件是django的门户
    1、请求来的时候需要先经过中间件才能到达真正的django后端
    2、响应走的时候最后也需要经过中间件才能发送出去
"""

django请求生命周期图

# 研究django中间件代码规律


"""
django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法
    1、必须要掌握的
        process_request
        
        process_response
    
    2、了解即可
        process_view
        
        process_template_response
        
        process_exception
        
"""

如何自定义中间件

"""
1、在项目名或者应用名下创建一个任意名称的文件夹
2、在该文件夹内创建一个任意名称的py文件
3、在该py文件内需要书写类(这个类必须要继承MiddlewareMixin)
    然后在这个类里面就可以自定义五个方法了
    (这五个方法并不是全部需要书写,用几个写几个)
4、需要将类的路径以字符串的形式注册到配置文件中才能生效
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # 处理session
    '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',
    '你自己写的中间件的路径1',
    '你自己写的中间件的路径2',
    '你自己写的中间件的路径3',
    ...
]

"""
"""
1、必须要掌握的
    process_request
        1、请求来的时候需要经过每一个中间件里面的process_request方法
           经过的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
        2、如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件
        3、如果该方法返回了HttpResponse对象,那么请求将不再继续执行
           而是直接原路返回(校验失败不允许访问...)
        process_request方法就是用来做全局相关的所有限制功能
        
    process_response
        1、响应走的时候需要经过每一个中间件里面的process_response方法
           该方法有两个额外的参数request,response
        2、该方法必须返回一个HttpResponse对象
            1、默认返回的是形参response对象
            2、你也可以返回自己的
        3、顺序是按照配置文件中注册了的中间件从下往上依次经过
            如果你没有定义的话,直接跳过执行下一个
            
    研究如果在第一个process_request方法就已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况
    是其他情况
        会就是会直接走同级别的process_response返回
        
    flask框架也有一个中间件但是它的规律
        只要返回了数据就必须经过所有中间件里面的类似于process_response方法
    
2、了解即可
    process_view
        路由匹配成功之后 执行视图函数之前
        会自动执行中间件里面的该方法
        顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
        
    process_template_response
        返回的HttpResponse对象有render属性的时才会触发
        顺序是按照配置文件中注册了的中间件从上往下依次经过
        
    process_exception    
        当视图函数中出现异常的情况下触发
        顺序是按照配置文件中注册了的中间件从上往下依次经过
"""
"""
第一步:导入
from django.utils.deprecation import MiddlewareMixin

第二步:自定义中间件
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

第三步:在view中定义一个视图函数(index)
def index(request):

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

"""

csrf跨站请求伪造

"""
钓鱼网站
    我搭建一个跟正规网站一模一样的界面(中国银行)
    用户不小心进入到了我们的网站,用户给某个人打钱
    用户打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了
    但是唯一不同的是打钱的账户不是用户想要的账户 
    变成了一个莫名其妙的账户
    
大学英语四六级
    考之前需要学生自己网站登录缴费
    
内部本质    
    我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框
    然后我们在内部隐藏一个已经写好的name和value的input框
    
如何规避上述问题
    csrf跨站请求伪造校验
        网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识
        当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识 如果唯一标识不对 直接拒绝(403 forbiden) 如果成功则正常执行
"""

如何符合校验(form和ajax)

# form表单如何符合校验
<form action="" method="post">
    {% csrf_token %}
    <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>

# ajax如何符合校验
 $('#d1').click(function () {
        $.ajax({
            url:'',
            type:'post',
            // 第一种 利用标签查找获取页面上的随机字符串
            {#data:{'username':'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#}
            // 第二种 利用模板语法提供的快捷书写
            {#data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
            // 第三种 通用方式  直接拷贝js代码 并引用到自己的html页面上即可
            data:{'username':'jason'},
            success:function () {

            }
        })
    })
            
# 第三种方式外部引入:
{% load static %}
<script src="{% static 'js/mysetup.js' %}"></script>
          
            
# mysetup.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);
    }
  }
});

csrf相关装饰器

"""
1、网站整体都不校验csrf,就单单几个视图函数需要校验
2、网站整体都校验csrf,就单单几个视图函数不校验
"""


from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect,csrf_exempt
"""
csrf_protect  需要校验
    针对csrf_protect符合我们之前所学的装饰器的三种玩法
csrf_exempt   忽视校验
    针对csrf_exempt只能给dispatch方法加才有效
"""


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

补充知识点

# 模块  importlib

from myfile import b
print(b)
# print(b.name)

import importlib
res = 'myfile.b'  
ret = importlib.import_module(res)  # from myfile import b
# 该方法最小只能到py文件名
print(ret)

重要思想

"""
import settings
import importlib

def send_all(content):
    for path_str in settings.NOTIFY_LIST:  # path_str 一个个字符串
        module_path,class_name = path_str.rsplit('.',maxsplit=1)
        # module_path = 'notify.email'  class_name='Email'
        # 1、利用字符串导入模块
        module = importlib.import_module(module_path)  # from notify import emali
        # 2、利用反射获取类名
        cls = getattr(module,class_name)  # Email,QQ,Wechat
        # 3、生成类的对象
        obj = cls()
        # 4、利用鸭子类型直接调用send方法
        obj.send(content)

"""

Auth模块

"""
其实我们在创建好一个django项目之后直接执行数据库迁移命令会自动生成很多表
    django_session
    
    auth_user

django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且还必须是管理员用户才能进入

创建超级用户(管理员)
    python3 manage.py createsuperuser
    
依赖于auth_user表完成用户相关的所有功能
"""

方法总结

# 1、比对用户名和密码是否正确
# 括号内必须同时传入用户名和密码
user_obj = auth.authenticate(request,username=username,password=password)
print(user_obj)  # 用户对象 zhzhang  数据不符合则返回None
# print(user_obj.username)  # zhzhang
# print(user_obj.password)  # 密文


# 2、保存用户状态
# 保存用户状态
auth.login(request,user_obj)  # 类似于request.session[key] = user_obj
# 只要执行了该方法 你就可以在如何地方通过request.user获取到当前登录的用户对象


# 3、判断当前用户是否登录
request.user.is_authenticated()
# 自动去django里面查找对应的用户对象给你封装到request.user中


# 4、获取当前登录用户
request.user   # 用户对象  AnonymousUser匿名用户


# 5、校验用户是否登录装饰器
from django.contrib.auth.decorators import login_required
# 局部配置
@login_required(login_url='/login/')
# 全局配置:没有登录跳转到指定的位置
LOGIN_URL = '/login/'
"""
1、如果局部和全局都有 该听谁的? 
    局部 > 全局
2、局部和全局哪个好呢?
    全局的好处在于 无需重复写代码 但是跳转的页面却很单一
    局部的好处在于 不同的视图函数在用户没有的登录的情况下可以跳转到不同的页面
"""

# 6、比对原密码
 # 校验老密码对不对
is_right = request.user.check_password(old_password)  # 自动加密比对密码


# 7、修改密码
request.user.set_password(new_password)
request.user.save()  # 这一步才是真正的操作数据库


# 8、注销
auth.logout(request)

# 9、注册
# 操作author_user表写入数据
User.objects.create(username=username,password=password)  # 写入数据 不能用create  密码没有加密处理

# 创建普通用户
 User.objects.create_user(username=username,password=password)
    
# 创建超级用户(了解即可):使用代码创建超级用户 邮箱是必填的 而用命令创建则可以不填
User.objects.create_superuser(username=username,email='123@qq.com',password=password)

如何扩展auth_user表

from django.db import models
from django.contrib.auth.models import User,AbstractUser
# Create your models here.

# 第一种:一对一关系  不推荐
# class UserDetail(models.Model):
#     phone = models.BigIntegerField()
#     user = models.OneToOneField(to='User')


# 第二种:利用面向对象的继承
class UserInfo(AbstractUser):
    """
    如果继承了AbstractUser
    那么在执行数据库迁移命令的时候auth_user表就不会再创建出来了
    而UserInfo表中会出现auth_user所有的字段外加自己扩展的字段
    这么做的好处在于你能够直接点击你自己的表 更加快速的完成操作及扩展

    前提:
        1、在继承之前没有执行过数据库迁移命令
            auth_user没有被创建,如果当前库已经创建了那么你就重新换一个库
        2、继承的类里面不要覆盖AbstractUser里面的字段名
            表里面有的字段都不要动 只扩展额外字段即可
        3、需要在配置文件中告诉django你要用UserInfo替代auth_user(******)
            AUTH_USER_MODEL = 'app01.USERINFO'
                                '应用名.表名'
    """
    phone = models.BigIntegerField()
    create_time = models.DateTimeField(auto_now_add=True)

    
"""
你如果自己写表替代了auth_user那么
auth模块的功能还是照常使用 参考的表也由原来的auth_user变成了UserInfo

BBS作业就是用的就是上述方式
"""
原文地址:https://www.cnblogs.com/ZhZhang12138/p/14875694.html