1.基于django中间件的拷贝思想
需求:实现三种通知功能 #方式一: #notify.py def send_email(content): print('邮箱通知:%s'% content) def send_msg(content): print('短信通知:%s'% content) def send_wechat(content): print('微信通知:%s'% content) #start.py from first.notify import * def send_all(content): send_msg(content) send_email(content) send_wechat(content) if __name__ == '__main__': send_all('后天是周末') #方式二: #lib文件夹 #bbb.py name='from bbb' #aaa.py import importlib res='lib.bbb' #利用字符串形式导入模块 md=importlib.import_module(res) #这句相当于 from lib import bbb print(md) #该模块字符串最小单位只能到文件名 #settings.py NOTIFY_LIST=[ 'notify.email.Email', 'notify.msg.Msg', 'notify.qq.Qq', 'notify.WeChat.WeChat', ] #start.py from notify import * send_all('周末了,哈哈哈哈') #notify 包文件 #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) #qq.py class Qq(object): def __init__(self): pass def send(self, content): print('qq通知:%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 in settings.NOTIFY_LIST: #拿到一个个的路径字符串 'notify.email.Email' module_path,cls_name=path.rsplit('.',maxsplit=1) #字符串'notify.email' 类cls_name='Email' md=importlib.import_module(module_path) #from notify import email cls=getattr(md,cls_name) #获取到文件夹中类的名字 obj=cls() #产生一个个类对象 obj.send(content) #鸭子类型
2.跨站请求伪造csrf
- 如何实现钓鱼网站
- 你在写form表单的时候 让用户填写的账户input并没有name属性,是你自己在内部偷偷隐藏了一个具有name属性的input框,并且value值是你自己的账户,然后将该标签隐藏了
- 模拟该现象的产生 创建两个django项目
#正经网站 #transfer.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 <p>这是正经网站</p> <form action="" method="post"> <p>username:<input type="text" name="username"></p> <p>target_account:<input type="text" name="target_user"></p> <p>money:<input type="text" name="money"></p> <input type="submit"> #钓鱼网站 #transfer.py def transfer(request): return render(request,'transfer.html') #transfer.html <p>这是假冒的网站</p> <form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username:<input type="text" name="username"></p> <p>target_account: <input type="text"> <input type="text" name="target_user" value="ldd" style="display: none"></p> <p>money:<input type="text" name="money"></p> <input type="submit">
- 如何解决钓鱼网站这种问题:只处理本网站提供的数据
- 那么如何判断当前请求是本网站发出的呢?
- 解决方法:网站在返回给用户一个form表单的时候 会自动在该表单隐藏一个input框,这个框
的value是一个随机字符串 但是网站能够记住每一个浏览器发送的随机字符串
以后在写form表单的时候 只需要在表单中写一个{% csrf_token %} <p>这是正经网站</p> <form action="" method="post"> {% csrf_token %} <p>username:<input type="text" name="username"></p> <p>target_account:<input type="text" name="target_user"></p> <p>money:<input type="text" name="money"></p> <input type="submit">
- 为了防止钓鱼网站恶意拦截,正常网站都只要接收到post请求,都会做csrf校验
3.ajax的post请求如何用csrf解决
- 方式一:较为繁琐
- 先在页面任意位置上书写{% csrf_token %}
- 然后在发送ajax请求的时候 通过标签查找获取随机字符串,添加到data自定义对象即可
- 方式二:较为简单
- 方式三:官网提供的文件 最通用的一种方式,用于前后端分离
- 直接新建js文件,放在static文件夹里,记得settings里面配一下。拷贝代码 导入即可
- 不需要做任何的csrf相关的代码书写
{% csrf_token %} <button id="d1">发送ajax</button> <script src="/static/setup.js"></script> //js文件相关 <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', // 第一种 data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()}, // 第二种 data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}, // 第三种 利用脚本文件 data:{'username':'jason'}, success:function (data) { alert(data) } }) }) </script>
静态文件夹里的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 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');
将这些代码的文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的)
4.csrf相关的装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect # @csrf_exempt # 不校验 csrf def index(request): return HttpResponse('index') @csrf_protect # 校验 def login(request): return HttpResponse('login')
- 这两个装饰器在CBV上有何异同
# @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法 @method_decorator(csrf_exempt,name='dispatch') # csrf_exempt class MyIndex(views.View): # @method_decorator(csrf_exempt) # 可以 def dispatch(self, request, *args, **kwargs): return super().dispatch(request,*args,**kwargs) def get(self,request): return render(request,'transfer.html') # @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法 def post(self,request): return HttpResponse('OK') # csrf_exempt这个装饰器只能给dispatch装才能生效 """ csrf_protect方式全都可以 跟你普通的装饰器装饰CBV一致 """ # @method_decorator(csrf_protect,name='post') # 可以 class MyIndex(views.View): @method_decorator(csrf_protect) def dispatch(self, request, *args, **kwargs): return super().dispatch(request,*args,**kwargs) def get(self,request): return render(request,'transfer.html') # @method_decorator(csrf_protect) # 可以 def post(self,request): return HttpResponse('OK')
5.django源码剖析
- django有两个配置文件,一个是暴露给用户可以配置的,一个是内部全局的(用户配置了就用用户的,用户没有配置就用自己的)
- 先加载全局配置 给对象设值;然后加载局部配置 再给对象设值;一旦有重复的项,后者覆盖前者。
from django.conf import global_settings from django.conf import settings
6.auth模块
使用auth模块的时候,要用就用它的全套
如何创建超级用户(root)
python manage.py createsuperuser
- auth模块常用方法:
1.创建用户 from django.contrib.auth.models import User # 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') # 创建超级用户 需要邮箱数据 2.校验用户名和密码是否正确 from django.contrib import auth user_obj = auth.authenticate(request,username=username,password=password) # 必须传用户名和密码两个参数缺一不能 3.保存用户登录状态 auth.login(request,user_obj) # 只要这句话执行了 后面在任意位置 只要你能拿到request你就可以通过request.user获取到当前登录的用户对象 4.判断当前用户是否登录 request.user.is_authenticated() 5.校验原密码是否正确 request.user.check_password(old_password) 6.修改密码 request.user.set_password(new_password) request.user.save() # 千万不要忘了 7.注销 auth.logout(request) 8.校验用户是否登录装饰器 from django.contrib.auth.decorators import login_required 局部配置 @login_required(login_url='/login/') def index(request): pass 全局配置 settings配置文件中 直接配置 LOGIN_URL = '/login/' @login_required def index(request): pass # 如果全局配置了 局部也配置 以局部的为准
7.扩展auth_user表字段
方式1 利用一对一外键字段关系 class UserDetail(models.Model): phone = models.BigIntegerField() user = models.OneToOneField(to='User') 方式2 利用继承关系 from django.contrib.auth.models import AbstractUser class Userinfo(AbstractUser): phone = models.BigIntegerField() register_time = models.DateField(auto_now_add=True) # 一定要注意 还需要去配置文件中配置 AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名 #之前所有的auth模块功能也全适用于扩展好的表