首先django自带7个中间件,每个中间件都有各自对应的功能
并且django还支持程序员自定义中间件
在用django开发项目的时候,只要涉及到全局相关的功能都可以使用中间件方便的完成
- 全局用户身份校验
- 全局用户权限校验
- 全局访问频率校验
django中间件是django的门户
1. 请求来的时候需要先经过中间件才能够到达真正的django后端
2. 响应走的时候最后也需要经过中间件才能发送出去
django请求声明周期流程图
研究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', ]
django支持自定义中间件并且暴露5个可以自定义的方法
1. 必须要掌握
process_request
process_response
2. 了解即可
process_view
process_template_response
process_exception
1. 在项目名或者应用名下创建一个任意名称的文件夹
2. 在该文件夹内创建一个任意名称的py文件
3. 在该py文件内需要书写类(这个类必须继承MiddleMixin)
然后在这个类里面就可以自定义5个方法了
(这5个方法并不是全部都需要书写,用几个写几个)
4. 需要将类的路径以字符串的形式注册到配置文件中才能生效
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', '自己写的中间件的路径', '自己写的中间件的路径', ]
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_view方法
经过的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
process_template_response
返回的HttpResponse对象有render属性的时候才会触发
顺序是按照配置文件中注册了的中间件从下往上依次经过
process_exception
当视图函数中出现异常的情况下触发
顺序是按照配置文件中注册了的中间件从下往上依次经过
钓鱼网站
搭建一个跟正规网站一模一样的界面(中国银行)
用户不小心进入到了这个网站,用户给某个人打钱
打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了
但是唯一不同的是打钱的账户不是用户想要打的账户 变成了一个莫名其妙的账户
大学英语四六级
考之前需要学生自己网站登录缴费
内部本质
我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框
然后我们在内部隐藏一个已经写好name和value的input框
如何规避上述问题
csrf跨站请求伪造校验
网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识
当这个页面朝后端发送post请求的时候 后端会先校验唯一标识,如果唯一标识不对则直接拒绝(403 forbidden)如果成功则正常执行
form表单如何符合校验
// 第一种 利用标签查找获取页面上的随机字符串 {#data:{"username":"jason","csrfmiddlewaretoken":$("[name=csrfmiddlewaretoken]").val()},#} // 第二种 利用模板语法提供的快捷书写 {#data:{"username":"jason",'csrfmiddlewaretoken':'{{csrf_token}}'},#} // 第三种 通用方式 直接拷贝js代码并引用到自己的html页面上即可 data: {"username": "jason2"},
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); } } });
1. 网站整体不校验csrf,就单单几个视图函数需要校验
2. 网站整体都校验csrf,就单单几个视图函数不校验
from django.shortcuts import render,HttpResponse from django.views.decorators.csrf import csrf_protect,csrf_exempt """ csrf_protect 需要校验 针对csrf_protect符合我们之前所学的三种装饰器 csrf_exempt 忽视校验 针对csrf_exempt 只能给dispatch加才有效 """ # Create your views here. def index(request): return HttpResponse('INDEX') # @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('money') print('%s给%s转了%s元'%(username,target_user,money)) return render(request,'transfer.html') from django.views import View from django.utils.decorators import method_decorator # @method_decorator(csrf_protect,name='post') # 针对csrf protect 第二种方式也可以 # @method_decorator(csrf_exempt,name='post') # 针对csrf exempt 第二种方式也不可以 @method_decorator(csrf_exempt,name='dispatch') class MyCsrfToken(View): # @method_decorator(csrf_protect) # 针对csrf protect 第三种方式也可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以 def dispatch(self, request, *args, **kwargs): return super().dispatch(request,*args,**kwargs) def get(self,request): return HttpResponse('GET') # @method_decorator(csrf_protect) # 针对csrf_protect 第一种方式可以 @method_decorator(csrf_exempt) # 针对csrf_exemt 第一种方式不可以 def post(self,request): return HttpResponse('POST')
def send_all(content): for path_str in settings.NOTIFY_LIST: #path_str = notify.email.Email module_path, class_name = path_str.rsplit('.',maxsplit=1) # print('module_path:',module_path) # print('class_name:',class_name) # module_path = 'notify.email' # class_name = 'Email' # 1. 利用字符串导入模块 module = importlib.import_module(module_path) # from notify import email # print(module) # 2. 利用反射获取类名 cls = getattr(module,class_name) # print(cls) # 3. 生成类的对象 obj = cls() # 4. 利用鸭子类型直接调用send方法 obj.send(content)
其实我们在创建好一个django项目之后直接执行数据库迁移命令会自动生成很多表
django_session
auth_user
django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且还必须是管理员用户才能进入
创建超级用户(管理员)
python manage.py createsuperuser
1. 比对用户名和密码是否正确
user_obj = auth.authenticate(request,username=username,password=password) print(user_obj) # 用户对象 jeffrey 数据不符合则返回None print(type(user_obj)) # print(user_obj.username) #jeffrey # print(user_obj.password) #pbkdf2_sha256$260000$PmMuCeYOB2yXxV5Ps49uYS$/FkzF4Wwmogm4YOVpLYjg8+MgZLoYcae2ydcIT3ZWLQ=
2. 保存用户状态
auth.login(request,user_obj) # 类似于request.session[key] = user_obj
3. 判断当前用户是否登录
request.user.is_authenticated
4. 获取当前登录用户
request.user
5. 校验用户是否登录装饰器
from django.contrib.auth.decorators import login_required # 局部配置 @login_required(login_url='/login/') # 局部配置: 用户没有登录跳转到login_url后面指定的网址 # 全局配置 LOGIN_URL = '/login/' """ 1. 如果局部和全局都有 该听谁的 局部 > 全局 2. 局部和全局哪个好呢? 全局的好处在于无需重复写代码 但是跳转的页面却很单一 局部的好处在于不同的视图函数在用户没有登录的情况下可以跳转到不同的页面 """
6. 比对原密码
is_correct = request.user.check_password(old_password)
7. 修改密码
request.user.set_password(new_password) #仅仅是在修改对象的属性 request.user.save() # 这一步才是真正的操作数据库
8. 注销
auth.logout(request)
9. 注册
# 操作auth_user表吸入数据 # User.objects.create(username=username,password=password) #写入数据 不能用create 密码没有加密处理 # 创建普通用户 # User.objects.create_user(username=username,password=password) # 创建超级用户 User.objects.create_superuser(username=username,password=password)
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()
如果自己写表替代了auth_user那么
auth模块的功能还是照常使用,参考的表也有原来的auth_user变成了UserInfo