[toc]html
今日内容: 基于配置文件的编程思想 跨站请求伪造csrf django auth模块 django settings源码剖析 基于django settings源码实现本身项目配置文件插拔式设计
cookie与session cookie与session发展史 因为http协议是无状态的 因此咱们须要发明一些可以保存用户的技术 cookie 保存在客户端浏览器上面的键值对(能够有多个) cookie是后端服务器控制与设值的 浏览器只是奉命行事 浏览器也有权利拒绝写入cookie(一切须要登陆认证的网站全都没法正常登陆) 如何查看浏览器上面的cookie application/COOKIES/信息 session 保存在服务端上的键值对(能够有多个) django如何操做cookie 前提是须要用到HttpResponse对象 obj = HttpResponse() obj1 = render() obj2 = redirect() 设值cookie obj.set_cookie('k1','v1') 获取cookie request.COOKIES.get('k1') 设值失效时间 obj.set_cookie('k1','v1',max_age=10) obj.set_cookie('k1','v1',expires=5) 都以秒为单位expires针对IE浏览器 删除cookie obj.delete_cookie('k1') 基于cookie实现用户登陆校验 装饰器 用户在没有登陆以前若是访问了一个须要登陆以后才能访问的页面 那么会先跳转到登陆页面 用户输入用户名和密码并正确的状况下 再自动跳转到以前用户想要访问的页面 1.request.path_info 2.利用get请求携带参数的方法 session操做 django默认的session失效时间是14天,你也能够设置 session是保存在服务端的 默认状况下 你的django须要先执行数据库迁移命令 由于保存sesson数据的表须要先提早建立出来,若是不建立会报错(no such table:django_session) session不仅仅能够放在django_session表中 也能够放在任意可以做为数据库的地方 文件 内存 缓存 ... 配置文件 django的session表中的数据是针对浏览器的 同一个浏览器只会存一行数据 设置session request.session['k1'] = 'v1' """ 1.django内部自动调用算法 生成一个随机字符串 2.将随机字符串与设置的值保存到django_session表中(真正写入数据的操做是在django中间件里面的session相关中间件发生的) 3.将随机字符串返回给浏览器保存到cookie sessionid:随机字符串 """ 获取session request.session.get('k1') """ 1.django内部会自动获取浏览器发送过来的cookie中的获取sessionid所对应的随机字符串 2.拿着随机字符串去django_session表中比对 3.若是有拿出数据放到request.session中供用户调用 """ 删除session request.session.delete() request.session.flush() 失效时间 request.session.set_expiry() 1.数字(不能是0) 秒数 2.数字0 浏览器关闭自动失效 3.时间格式数据 datetime 4.不写 默认参考的是全局的失效时间 基于session实现用户登陆校验 django中间件 django中间件就相似于django的门户或者保安 全部的请求和响应都必须通过中间件才可以正常经过 而且在中间件中能够对请求和响应的数据进行处理 django中间件在设计到一些全局方面的功能时 做用很是大 1.网站全局的用户登陆校验 2.网站全局的访问频率校验 3.网站全局的用户权限校验 ... 只要是全局的功能,你都应该第一个想到中间件 django默认有七个中间件 每一个中间件都有本身独立的功能 若是你想用 你直接注释掉便可 django还支持用户自定义本身的中间件 而且暴露给用户五个能够自定义的方法 前提 1.只要是设计处处理前端业务逻辑的视图函数 都须要有形参request 2.若是你想自定义中间件 你必须写一个类而且继承MiddlewareMixin 3.一旦形参中reponse你就必须返回 由于response就是后端想要返回前端的数据 五个方法 须要掌握的 1.process_request(self,request) (******) 1.请求到来的时候 会按照配置文件注册的从上往下的顺序依次通过每个中间件里面的该方法 若是中间件中没有定义该方法 直接跳过执行下一个 2.一旦该方法返回了一个HttpResponse对象 那么请求再也不日后走 而是走到同级别的process_response依次返回 2.process_response(self,request,response) 1.响应走的时候 会按照配置文件注册的从下往上的顺序依次通过每个中间件里面的该方法 该方法默认必须返回response 若是你返回了本身的HttpResponse对象 那么用户收到的就是你的 须要了解的 3.process_view(self,request,view_name,*args,**kwargs) 1.路由匹配成功以后执行视图函数以前 4.process_exception(self,request,response) 1.视图函数中出现错误的时候自动触发 5.process_template_response(self,request,response) 1.返回的对象中含有render方法
利用字符串的形式导入模块前端
import importlib res = 'lib.bbb' md = importlib.import_moudle(res) print(md)
首先定义一个配置路径列表 NOTIFY_LIST = [ 'notify.email.Email', 'notify.msg.Msg', 'notify.wechat.WeChat' ] ------------------------------------------------------- 在包的__init__文件中设置send_all函数 # import settings import importlib def send_all(countent): # 将settings文件中的存储函数路径迭代循环 for path in settings.NOTIFY_LIST: # 经过.切割,获取每个文件的的路径,以及cls类名 module_path,cls_name = path.rsplit('.',maxsplit=1) # 经过importlib方法传入路径,获取导入文件对象 md = importlib.import_module(module_path) # getattr反射获取md文件中的相对应cls类 cls = getattr(md,cls_name) # 实例化类,获取一个个类的对象 obj = cls() # 调用类中的发送方法 obj.send(countent)
你本身写一个跟中国银行正规网站如出一辙的页面 用户输入用户名 密码 对方帐户 转帐金额提交 请求确实是朝中国银行的接口发送的 钱也扣了 可是对方帐户变了 变成了钓鱼网站本身提早设置好的帐户
你在写form表单的时候 让用户填写的对方帐户input并无name属性 而是你本身在内部偷偷隐藏了一个具备name属性的input框 而且value值是你本身的帐户 而后将该标签隐藏了
建立两个django项目 ------------------------------------------------------ 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_account" value="jason" style="display: none;"> // 经过设置两个input框,并隐藏自定义value的框,得到该白转帐人的目的 </p> <p>money:<input type="text" name="money"></p> <input type="submit"> ------------------------------------------------------ <p>这是正经的网站</p> <form action="" method="post"> <p>username<input type="text" name="username"></p> <p>target_account<input type="text" name="target_account"></p> <p>money:<input type="text" name="money"></p> <input type="submit"> </form>
django中的中间件`'django.middleware.csrf.CsrfViewMiddleware',`就是负责校验csrf的
只处理本网站发送的post请求python
如何识别如何判断当前请求是不是本网站发出的
**防护CSRF攻击:** 目前防护 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。
{% csrf_token %}
在from表单中添加 {% csrf_token %}
ajax
<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>
{% csrf_token %}
data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},
先在页面任意的位置上书写{% csrf_token %}
算法
直接在data中书写'csrfmiddlewaretoken':'{{ csrf_token }}
键值对数据库
data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},
官网提供的文件 最通用的一种方式django
script
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); } } });
将下面的文件配置到你的Django项目的静态文件中,在html页面上经过导入该文件便可自动帮咱们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件以前,须要先导入jQuery,由于这个配置文件内的内容是基于jQuery来实现的)编程
更多细节详见:Djagno官方文档中关于CSRF的内容后端
两个装饰器,分别制定视图函数方法哪一个须要校验csrf,或不须要校验的浏览器
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
@csrf_exempt # 不校验 csrf def index(request): return HttpResponse('index')
@csrf_protect
@csrf_protect # 校验 def login(request): return HttpResponse('login')
cbv
CBV就是在url中一个路径对应一个类 基于FBV的模式就是在Django的路由映射表里进行url和视图函数的关联,而基于CBV的模式则是在views.py文件中定义视图类,在视图类中视图函数,如get,post,put,delete等 在写代码中的几点注意事项: cbv定义类的时候必需要继承view 在写url的时候必需要加as_view 类里面使用form表单提交的话只有get和post方法 类里面使用ajax发送数据的话支持定义如下不少方法 restful规范: 'get'获取数据, 'post'建立新数据, 'put'更新, 'patch'局部更新, 'delete'删除, 'head', 'options', 'trace' ---------------------------------------------------- 视图函数中 from django.views import View class IndexView(View): # 以get形式访问会执行get函数,通常状况下获取数据 def get(self, *args, **kwargs): return HttpResponse('666') # 以post形式访问的话会执行post函数,通常状况下发送数据 def post(self, *args, **kwargs): return HttpResponse('999')
csrf_exempt这个装饰器只能给dispatch装才能生效
# @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_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')
另外的配置文件settings在 from django.conf import settings 中
一个是暴露给用户能够配置的 一个是内部全局的(用户配置了就用用户的 用户没有配就用本身的) obj obj.name = 'egon' # 全局 obj.name = 'jason' # 局部
思考题 参看django settings源码 应用到本身的项目中 在你的项目中 实现配置文件的插拔式设计 用户配置了就用用户的 用户没有配就用本身的
Auth模块是Django自带的用户认证模块:
咱们在开发一个网站的时候,无可避免的须要设计实现网站的用户系统。此时咱们须要实现包括用户注册、用户登陆、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
Django做为一个完美主义者的终极框架,固然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。
建立超级用户(root)
python3 manage.py createsuperuser tool中直接createsuperuser
create_user()
导入表
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') # 建立超级用户 须要邮箱数据
auth.authenticate
导入auth模块
from django.contrib import auth
校验用户名和密码是否正确 user_obj = auth.authenticate(request,username=username,password=password) # 必须传用户名和密码两个参数缺一不能
login(request,对象)
auth.login(request,user_obj) # 只要这句话执行了 后面在任意位置 只要你能拿到request你就能够经过request.user获取到当前登陆的用户对象
is_authenticated()
request.user.is_authenticated()
check_password(旧密码)
request.user.check_password(old_password)
set_password(新密码)
获取 request.user.set_password(new_password) request.user.save() # 千万不要忘了
logout(request)
auth.logout(request) # request.session.flush()
@login_required
from django.contrib.auth.decorators import login_required
能够自定义未登陆的报错跳转界面login_url = '/跳转链接/'
@login_required(login_url='/login/') def index(request): pass
settings配置文件中 直接配置 LOGIN_URL = '/login/' 而后在视图函数添加 @login_required def index(request): pass
若是全局配置了 局部也配置 以局部的为准
利用外键一对一进行扩展
class UserDetail(models.Model): phone = models.BigIntegerField() user = models.OneToOneField(to='User')
利用面向对象的继承
导入AbstractUser类 from django.contrib.auth.models import AbstractUser 定义模型表类 class Userinfo(AbstractUser): phone = models.BigIntegerField() register_time = models.DateField(auto_now_add=True) settings配置文件中设置 AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名 执行数据库迁移命令 # 这么写完以后 以前全部的auth模块功能全都以你写的表为准
基于django settings配置文件是实现插拔式设计
# 执行start文件中 if __name__ == '__main__': 设置全局大字典的键值 键(随便) 值(文件的路径) os.environ['xxx'] = 'conf.settings' form lib.conf import settings print(settings.NAME) 有两个settings配置文件,分别对应暴露给用户,以及隐藏的全局配置 各自有NAME属性 ------------ conf.settings.__init__配置------------------- class Settings(object): def __init__(self): # 获取全局配置中的全部变量名 for name in dir(global_settings): # 筛选出须要的大写变量名 if name.isupper(): # 给self对象设置全局配置中的大写变量名 属性值 setattr(self,name,getattr(globals_settings,name)) # 获取暴露给用户的配置文件字符串路径 module_path = os.environ.get('xxx') md = importlib.import_module(module_path) for name in dir(md): if name.isupper(): k = name v = getattr(md,name) setattr(self,k,v) # 实例化类得到对象 settings = Settings()