一、模拟实现中间件的编程思想
(一)impotlib模块
- importlib模块可以通过字符串的形式导入模块
- importlib模块也支持
notify.email
的from···import类型 - 最小单位只能到文件名,不能用来导入文件中的变量名
# importlib模块(以导入json模块为例) import importlib json = importlib.import_module('json') module = importlib.import_module('notify.email')
(二)实现功能的配置使用
- 基于Django中间件的思想实现功能的配置使用
- 可以通过在settings.py中注释某个字符串来动态取消或增加某个功能的实现
# settings.py NOTIFY_LIST = [ 'notify.email.Email', 'notify.msg.Msg', 'notify.wechat.WeChat', 'notify.qq.Qq', ] # noyify/eamil.py class Email(object): def __init__(self): pass # 发送邮件需要的前期准备 def send(self,content): print('邮件通知:%s'%content) # noyify/msg.py class Msg(object): def __init__(self): pass # 发送短信需要的前期准备 def send(self,content): print('短信通知:%s'%content) # noyify/wechat.py class WeChat(object): def __init__(self): pass # 发送微信需要的前期准备 def send(self,content): print('微信通知:%s'%content) # noyify/__init__.py import settings import importlib def send_all(content): for path in settings.NOTIFY_LIST: module_name,cls_name=path.rsplit('.',maxsplit=1) # 因为import最小单位只能到文件,因此需要将文件路径和类名分隔开 module = importlib.import_module(module_name) # 通过importlib导入文件 cls = getattr(module,cls_name) # 通过反射获取文件中类 obj = cls() obj.send(content) # start.py from notify import * send_all('明天放假')
二、跨站请求伪造CSRF
(一)由来
以钓鱼网站的转账为例:
- 做一个假的网站,使得用户填写的对方账户iniput标签没有name属性
- 在内部隐藏一个具有name属性input标签,value为自己的账户
- 此时提交时,后端获取的目标账户是自己的账户,从而形成钓鱼
因此,CSRF就是为了解决这种问题
- 网站在返回用户form表单时,自动隐藏一个input标签,该标签value为一个随机的字符串
- 每一个浏览器发送的随机字符串都是独一无二的
- 从而解决这种调阅问题
(二)form表单的CSRF
- 只需要在表单中写一个{% csrf_token %}
- 浏览器中的隐藏input标签中
name="csrfmiddlewaretoken"
- 每一次刷新,value值都会不一样
<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"> </form> <!--浏览器可以看到的隐藏的input标签--> <input type="hidden" name="csrfmiddlewaretoken" value="rJ47FeK9T55wavvVJGY6UxdM1kTMHhTqotGfaXjXIK8Ahz2Uvs02yR9T8bBn5q2D">
(三)ajax中的CSRF
(1)通过data携带
- 在html页面任意地方书写{% csrf_token %}
- 通过标签查找将随机字符串添加到ajax的data对象中或者直接调用模板语法即可
data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()}, data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},
(2)通过headers携带
通过获取返回的cookie中字符串,放置在请求头中
$.ajax({ url: "/cookie_ajax/", type: "POST", headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 从Cookie取csrf_token,并设置ajax请求头 data: {"username": "Q1mi", "password": 123456}, success: function (data) { console.log(data); } })
(3)官网提供的文件(推荐用法)
- static文件夹下新建一个任意名JS文件(setup.js为例),拷贝以下代码
- 在页面中导入该js文件
# jswenjian 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相关的装饰器
- 局部:CSRF相关的装饰器用于局部设置或取消CSRF验证
- 全局: 通过中间件 django.middleware.csrf.CsrfViewMiddleware 来设置或取消CSRF校验
(1)MTV模型中使用
- 需要先导入装饰器
- @csrf_protect :设置被装饰函数的CSRF(跨站请求伪造)校验
- @csrf_exempt:取消被装饰函数的CSRF(跨站请求伪造)取消
from django.views.decorators.csrf import csrf_exempt,csrf_protect
(2)CBV模型中使用
- csrf_protect装饰器CBV中四种装饰器使用方式都可以生效,
- csrf_exempt装饰器只能给dispatch装才能生效
# 1. 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') # 2. csrf_exempt这个装饰器只能给dispatch装才能生效 # @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法 @method_decorator(csrf_exempt,name='dispatch') # 生效 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')
三、Django中auth模块
(一)什么是auth模块
- auth模块是Django自带的用户认证模块,可以实现包括用户登录、注册、认证、注销、修改密码等功能
- 默认使用auth_user表存储用户数据
- 使用之前先导入
from django.contrib import auth
(二)常用方法
(1)创建用户
- create_superuser(): 创建超级用户 ,必要参数username、password、email
- create_user():创建普通用户,必要参数username、password
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)校验用户
- authenticate(): 验证用户名以及密码是否正确 , 需要username 、password两个关键字参数 ,正确返回User对象,否则为空
from django.contrib import auth user_obj = auth.authenticate(request,username=username,password=password) # 必须传用户名和密码两个参数缺一不能
(3)登录保存
- login(HttpRequest, user):用户登录并保存状态,保存后可以通过request.user中获取当前登录用户对象
auth.login(request,user_obj) # 只要这句话执行了 后面在任意位置 只要你能拿到request你就可以通过request.user获取到当前登录的用户对象
(4)是否登录
- is_authenticated(): 判断当前请求是否通过了认证
request.user.is_authenticated()
(5)检验密码
- check_password(password): 检查密码是否正确的方法,需要提供当前请求用户的密码 ,返回布尔值
request.user.check_password(old_password)
(6)修改密码
- set_password(password):设置密码,修改完之后一定记得使用save保存
request.user.set_password(new_password) request.user.save() # 千万不要忘了
(7)注销
- logout(request): 清除当前请求的全部session信息, 无返回值
auth.logout(request)
(8)登录检验装饰器
- login_requierd(): 一个登录校验的装饰器,可以添加变量login_url指定没有登录情况下跳转的页面
- 局部配置:直接在括号内添加页面地址
- 全局配置:settings文件中配置LOGIN_URL参数
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 # 如果全局配置了 局部也配置 以局部的为准
(9)User对象的方法
- i s_staff : 用户是否拥有网站的管理权限
- is_active : 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录。
(三)扩展默认的auth_user表
(1)一对一外键
通过一对一外键字段关系扩展字段
class UserDetail(models.Model): phone = models.BigIntegerField() user = models.OneToOneField(to='User')
(2)继承
- 自定义一个继承内置的AbstractUser的Model类 (需要先导入)
- settings配置文件中配置AUTH_USER_MODEL参数(应用名.表名)
# module.py from django.contrib.auth.models import AbstractUser class Userinfo(AbstractUser): phone = models.BigIntegerField() register_time = models.DateField(auto_now_add=True) # settings.py AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名
四、Django中settings源码
- Django中有两个配置文件,一个内部全局的,一个用户自定义
- 如果用户配置了,就使用该配置,否则使用内部全局设置
实现思路:先加载全局配置给对象设置,然后在加载局部配置 再给对象设置,一旦有重复的项 后者覆盖前者
五、文件插拔式设计
基于settings源码设计思想启发,实现文件插拔式设计
# conf/settings.py NAME = '我是暴露给用户的自定义配置' # lib/conf/globa_settings.py NAME = '我是项目默认的配置文件' # lib/conf/__init__.py import importlib from lib.conf import global_settings import os class Settings(object): def __init__(self): for name in dir(global_settings): if name.isupper(): setattr(self, name, getattr(global_settings, name)) # 获取暴露给用户的配置文件字符串路径 module_path = os.environ.get('xxx') md = importlib.import_module(module_path) # md = settings for name in dir(md): if name.isupper(): k = name v = getattr(md,name) setattr(self,k,v) settings = Settings() # start.py import os import sys BASE_DIR = os.path.dirname(__file__) sys.path.append(BASE_DIR) if __name__ == '__main__': # os.environ.setdefault('xxx','conf.settings') os.environ['xxx'] = 'conf.settings' # environ是一个字符串所对应环境的映像对象 from lib.conf import settings print(settings.NAME)
来源:https://www.cnblogs.com/faye12/p/11993193.html