5.Django扩展
Django 中间件
1、中间件
中间件是一个钩子框架,它们可以介入Django 的请求和响应处理过程。它是一个轻量级、底层的“插件”系统,用于在全局修改Django 的输入或输出。
每个中间件组件负责完成某个特定的功能。
Django在中间件中预置了六个方法,这六个方法的区别在于不同的阶段执行,对输入或输出进行干预,方法如下: 1.初始化:无需任何参数,服务器响应第一个请求的时候调用一次,用于确定是否启用当前中间件
- Django初始化你的中间件无需任何参数,因此不要定义一个有参数的init方法。
- 不像process* 对每个请求都要调用,_init 只会被调用一次,就是在Web 服务器响应第一个请求的时候。
2.处理请求前:在每个请求上调用,返回None或HttpResponse对象 在process_request方法中,返回值为HttpResponse类型的对象的时候不交给普通的视图,直接返回给浏览器,返回值为None的时候,请求处理完之后交给普通的视图处理。def __init__(self): pass
3.处理视图前:在每个请求上调用,返回None或HttpResponse对象def process_request(self,request): pass
它将返回None 或一个HttpResponse 对象。如果返回None ,django将继续执行,如果返回的是一个HttpResponse,将不调用视图,直接返回HttpResponse.
def process_view(self,request, view_func, view_args, view_kwargs):
pass
4.处理模板响应前:在每个请求上调用,返回实现了render方法的响应对象
def process_template_response(self,request, response):
pass
5.处理响应后:所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象
def process_response(self,request, response):
pass
6.异常处理:当视图抛出异常时调用,在每个请求上调用,返回一个HttpResponse对象
def process_exception(self,request,exception):
pass
2、激活中间件
要激活一个中间件组件,需要把它添加到 setting.py 配置文件中的 MIDDLEWARE_CLASSES 元组中。 创建项目的时候django有一些默认的中间件,在前面讲post请求的时候有提到过csrf,就是django的中间件。 Django的程序中,中间件不是必需的,但是django默认的一般不要关闭.
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
3、自定义中间件
每个中间件组件是一个独立的Python 类,你可以定义下面这些方法中的一个或多个:
在personal_blog/目录下创建middleware.py文件,代码如下
#coding=utf-8
class MyMiddleware:
def __init__(self):
print ('--------------init')
def process_request(self,request):
print ('--------------request')
def process_view(self,request, view_func, view_args, view_kwargs):
print ('--------------view')
def process_template_response(self,request, response):
print ('--------------template')
return response
def process_response(self,request, response):
print ('--------------response')
return response
- 注意: 如果你使用的不是 1.8版本的django,比如:1.10版本,其他版本没测试。按照上面的方法写中间件会报错。
报错信息如下:
解决办法: 自定义中间件的之后需要继承*** Operational MODE: preforking+threaded *** Traceback (most recent call last): File "single_blog/wsgi.py", line 16, in <module> application = get_wsgi_application() File "/home/python/.virtualenvs/py3/lib/python3.5/site-packages/django/core/wsgi.py", line 14, in get_wsgi_application return WSGIHandler() File "/home/python/.virtualenvs/py3/lib/python3.5/site-packages/django/core/handlers/wsgi.py", line 153, in __init__ self.load_middleware() File "/home/python/.virtualenvs/py3/lib/python3.5/site-packages/django/core/handlers/base.py", line 82, in load_middleware mw_instance = middleware(handler) TypeError: object() takes no parameters unable to load app 0 (mountpoint='') (callable not found or import error)
MiddlewareMixin
,其他用法还是一样from django.utils.deprecation import MiddlewareMixin class MyMiddleware(MiddlewareMixin): def process_request(self,request) pass
写好中间件类之后,我们需要将自定义中间件注册到setting.py 配置文件中的 MIDDLEWARE_CLASSES
从新启动服务器,去访问可以看到方法的调用循序。
中间件在一个请求中的执行顺序:
中间件的应用:拦截器,当发现一个人恶意访问的时候,对他进行拦截,返回一个错误页给他。
blacklist =['120.196.164,25','152.26.135.12']
class BlockedIpMiddleware(object):
def process_request(self, request):
if request.META['REMOTE_ADDR'] in blacklist:
return http.HttpResponseForbidden('<h1>Forbidden</h1>')
request.META['REMOTE_ADDR'] 获取请求者的ip,判断这个用户ip是否在黑名单中。
当然这里没有实现黑名单统计,简单实用一个列表代替。
Django 缓存系统
Django 是动态网站,一般来说需要实时地生成访问的网页,展示给访问者,如果数据每次都是从数据库中读取的话,会比在内存中,或者硬盘中读取的慢的多。
缓存系统工作原理: Django接收到请求后,尝试从缓存中找到网址对应资源,如果页面在缓存中,直接返回缓存的页面,如果缓存中没有,一系列操作(比如查数据库)后,保存生成的页面内容到缓存系统以供下一次使用,然后返回生成的页面内容。
Django提供了不同级别的缓存粒度:你可以缓存特定视图的输出、也可以缓存整个网站。
1、缓存系统
使用缓存需要在setting.py中 CACHES
配置来实现:
利用文件系统缓存:
基于文件的缓存后端序列化和存储每个缓存值作为一个单独的文件。 BACKEND:指定使用的缓存系统 LOCATION:缓存文件的存放路径
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', #指定使用文件系统
'LOCATION': '/var/tmp/django_cache', # 保存路径
}
}
}
本地内存缓存: 如果服务器内存足够大,需要缓存的内容不是非常多的话,利用本地内存缓存也是一个不错的选择。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
数据库缓存: Django 可以把缓存保存在你的数据库里。如果你有一个快速的、专业的数据库服务器的话.
LOCATION: 数据库中表的名字。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}
使用数据库缓存之前,必须用这个命令来创建缓存表:
python manage.py createcachetable
这条命令会自动创建Django的基于数据库缓存系统预期的特定格式的数据表,表名为LOCATION设置的名字。
CACHES 参数:
每一个缓存系统都可以额外设置一些参数。
常用参数: TIMEOUT:缓存的默认过期时间,以秒为单位, 这个参数默认是 300 seconds (5 分钟).
OPTIONS:
- MAX_ENTRIES:高速缓存允许的最大条目数,超出这个数则旧值将被删除. 这个参数默认是300.
- CULL_FREQUENCY:当达到MAX_ENTRIES 的时候,被删除的条目比率。 实际比率是 1 / CULL_FREQUENCY,默认设置是3,如果设置为2的话,那就是删除一半。
比如设置一个文件缓存系统,超时时间为60s,缓存最大条目为1000,如果遇到非法参数,django会自动忽略掉。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'TIMEOUT': 60,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}
缓存与未缓存对比:
未缓存
from django.shortcuts import render
def index(request):
# 读取数据库或者其他操作,渲染模板
return render(request, 'index.html', {'queryset':queryset})
这样访问看似没什么问题,但是访问量大就会有非常多的访问请求,或者视图函数有些算法比较耗时,给用户的感觉就是网站很慢。
使用缓存: 访问一个网址时, 尝试从 cache 中找有没有缓存内容 如果网页在缓存中显示缓存内容,否则生成访问的页面,保存在缓存中以便下次使用,显示缓存的页面。
根据访问的url,查找页面是否在缓存,
if the page is in the cache:
return the cached page
else:
# 读取数据库或者其他操作,渲染模板
return render(request, 'index.html', {'queryset':queryset})
2、站点级缓存
站点级缓存指的是整个网站进行缓存。
设置整站缓存需要将:
'django.middleware.cache.UpdateCacheMiddleware'
'django.middleware.cache.FetchFromCacheMiddleware'
两个中间件添加到 MIDDLEWARE_CLASSES 设置 注意: 'UpdateCacheMiddleware'中间件,必须放在列表的开始位置,而FetchFromCacheMiddleware中间件,必须放在最后。
注意:设置了缓存之后,在缓存没有超时前,在后台更新数据之后访问的还是缓存的内容,更新内容得等到缓存超时之后再次缓存才能更新数据。
3、单个view缓存
更加轻巧的缓存方法是对单个有效视图的输出进行缓存。 django.views.decorators.cache 定义了一个自动缓存视图响应的 cache_page 装饰器
单个视图缓存,要先导入 cache_page
from django.views.decorators.cache import cache_page
@cache_page(60*15)
def index(request):
post_list = Post.post_object.all()
return render(request, 'personal_blog/index.html', context={"post_list": post_list})
装饰器参数是时间,单位是秒。
4、模板片段缓存
如果不想整个视图缓存,也可以在模板中对其中一块内容进行缓存。
在模板使用`cache`模板标签来缓存模板的一个片段。 要让模板处理这个标签,把{% load cache %} 放在缓存片段的上面。
标签{% cache time name %}将按给定的时间缓存包含块中的内容。它最少需要两个参数:缓存时间(以秒为单位);给缓存片段起的名称。
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
Django 发送邮件
1、邮箱设置
我们经常会遇到发送的邮件的功能,如果在注册一个网站的时候,注册成功网站会自动发送一封邮件给我们。或者用户提交一些建议与意见的时候网站直接发送一封邮件到管理员邮箱,而不用管理员登录网站后台去查看。 Django发送邮件模块在 django.core.mail 模块 send_mail 发送邮件需要使用SMTP服务器,常用的免费服务器有:163、126、QQ,下面以163邮件为例:
一般邮箱注册好之后是不允许第三方客户端登录的,需要进行开启POP3/SMTP服务,并对客户端授权。
登录注册好的163邮箱:
设置授权码:
2、项目配置
在django项目配置中设置发送者的邮箱地址:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com' # 163邮箱服务器地址
EMAIL_PORT = 25 # smtp端口
#发送邮件的邮箱
EMAIL_HOST_USER = 'pydjango@163.com'
#在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'zxcv1234'
#收件人看到的发件人
EMAIL_FROM = 'python<pydjango@163.com>'
3、send_mail 方法
发送邮件:
send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)
send_mail参数说明:
subject:邮件主题 message:邮件文本内容 from_email:发送者邮箱 recipient_list:一个由邮箱地址组成的字符串列表,recipient_list 中的每一个成员都会在邮件信息的“To:”区域看到其它成员,相当于群发。 fail_silently: auth_user: 可选的用户名用来验证SMTP服务器。如果没有提供这个值, Django 将会使用settings中 EMAIL_HOST_USER 的值。
auth_password: 可选的密码用来验证 SMTP 服务器。如果没有提供这个值, Django 将会使用settings中 EMAIL_HOST_PASSWORD 的值。 connection: 可选的用来发送邮件的电子邮件后端。如果没有指定,将使用缺省的后端实例。 html_message: 发送html格式的邮件
subject,message,from_email,recipient_list 四个参数是必选参数。
案例:
注意:参数 message跟html_message同时存在的话将使用 html_messae 参数,会自动忽略掉message 参数。
def send(request):
from django.core.mail import send_mail,send_mass_mail
from blog import settings
try:
html_message = '<a href="http://127.0.0.1:8000/blog/index/">点击激活</a>'
send_mail('来自django的邮件','这邮件是发自django',settings.EMAIL_FROM,
['pydjangos@16sd3.com'],html_message=html_message,fail_silently=False
)
except Exception as e:
return HttpResponse(e) # 如果发送邮件异常,返回错误信息。
else:
return HttpResponse('发送成功')
# settings.EMAIL_FROM 从setting配置中获取发送者的地址。
配置url:
url(r'^send/$', views.send),
访问url执行视图返回 发送成功 成功之后到接收者邮箱查看,可以看到来自django的的邮件。
4、send_mass_mail 方法
send_mass_mail()用来处理大批量邮件任务
send_mass_mail()和 send_mail()的主要差别是 send_mail() 每次运行时打开一个到邮箱服务器的连接,而 send_mass_mail() 对于所有的信息都只使用一个连接。这使得 send_mass_mail() 更高效点.
例如,以下代码将向两个不同的收件人集发送两个不同的消息;然而,只有一个到邮件服务器的连接将被打开:
message1 = ('主题', '内容', 'pydjango@163.com', ['first@example.com', 'other@example.com'])
message2 = ('主题', '内容','pydjango@163.com', ['second@test.com'])
send_mass_mail((message1, message2), fail_silently=False)
Django 后台嵌入富文本编辑器
1、django后台引入 kindeditor
由于django后台管理没有富文本编辑器,看着好丑,展示出来的页面不美观,无法做到所见即所得的编辑方式,所以我们需要引入第三方富文本编辑器。
1、到官网下载 kindeditor
这节我们学习使用的是 kindeditor 富文本编辑器。之所以选择这个编辑器主要看是它功能齐全还美观,下面这张图是kindeditor的样子。但是kindeditor对视频的支持性不太好,如果我们不需要上传视频可以选择 kindeditor 编辑器。如果需要上传视频,可以选择百度编辑器ueditor
。
2、下载好后删除这些没有的文件asp,asp.net,jsp,php文件在django中这些没用。
3、将删除后的文件引入自己的项目中。根目录下的static/js/editor/
4、在kindeditor 目录下新建一个config.js文件写入这段代码:
KindEditor.ready(function (k) {
window.editor = k.create('#id_content',{
resizeType:1,
allowPreviewEmoticons : true,
allowImageRemote : true,
uploadJson : '/blog/upload/kindeditor',# 处理图片的试图url
width:'700px',
height:'400px', # 窗口大小
# 定制功能按钮
items : [
'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',
'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright',
'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
'superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/',
'formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold',
'italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage',
'insertfile', 'table', 'hr', 'emoticons', 'baidumap', 'pagebreak',
'anchor', 'link', 'unlink', '|', 'about'
]
});
});
});
})
5、在admin中注册模型类的时候引入js文件
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
....省略其他代码
class Media:
# 在管理后台的HTML文件中加入js文件, 每一个路径都会追加STATIC_URL/
js = (
'/static/js/editor/kindeditor/kindeditor-all-min.js',
'/static/js/editor/kindeditor/zh_CN.js',
'/static/js/editor/kindeditor/config.js',
)
6、引入js文件之后打开后台就可以看到富文本编辑框了。
7、应用中创建一个文件名为uploads.py 的模块,
这里是在我的blog应用中,创建好后将下面这段代码复制到文件中,用来处理上传的图片。
from django.http import HttpResponse
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
import os
import uuid
import json
import datetime as dt
@csrf_exempt
def upload_image(request, dir_name):
result = {"error": 1, "message": "上传出错"}
files = request.FILES.get("imgFile", None)
if files:
result = image_upload(files, dir_name)
return HttpResponse(json.dumps(result), content_type="application/json")
# 目录创建
def upload_generation_dir(dir_name):
today = dt.datetime.today()
dir_name = dir_name + '/%d/%d/' % (today.year, today.month)
#if not os.path.exists(settings.MEDIA_ROOT + dir_name):
#os.makedirs(settings.MEDIA_ROOT + dir_name)
return dir_name
# 图片上传
def image_upload(files, dir_name):
# 允许上传文件类型
allow_suffix = ['jpg', 'png', 'jpeg', 'gif',
'bmp', 'zip', "mid", "avi",
"mpg", "asf",
"rm", "rmvb", "doc", "docx",
"xls", "xlsx", "ppt", "htm",
"html", "txt", "zip", "rar",
"gz", "bz2"]
file_suffix = files.name.split(".")[-1]
if file_suffix not in allow_suffix:
return {"error": 1, "message": "图片格式不正确"}
relative_path_file = upload_generation_dir(dir_name)
path = os.path.join(settings.MEDIA_ROOT, relative_path_file)
if not os.path.exists(path): # 如果目录不存在创建目录
os.makedirs(path)
file_name = str(uuid.uuid1()) + "." + file_suffix
path_file = os.path.join(path, file_name)
file_url = settings.MEDIA_URL + relative_path_file + file_name
open(path_file, 'wb').write(files.file.read())
return {"error": 0, "url": file_url}
配置上传图片视频的url
在应用的urls.py中导入刚才写的视图文件uploads.py
from blog.uploads import upload_image
url(r'^upload/(?P<dir_name>[^/]+)$', upload_image, name='upload_image'),
上面这些步骤富文本编辑器应该可以正常使用了。
2、百度编辑器 ueditor
直接用pip安装的Ueditor是python2版本的,如果需要Python3版本就需要下载源码包
解压后在命令行中进入源码包,输入python setup.py install
配置settings 就是在INSTALLED_APPS添加’DjangoUeditor’即可
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'personal_blog',
'DjangoUeditor' # Ueditor
)
配置urls 在根urls.py中添加
url(r'^ueditor/',include('DjangoUeditor.urls' ))
在models.py中使用:将文章字段替换成编辑器
from DjangoUeditor.models import UEditorField
class Post(models.Model):
title = models.CharField(max_length=50) # 文章标题
# 将文章字段替换成百度编辑器
content = UEditorField(width=1000, height=300,toolbars="full",imagePath="", filePath="")
....省略下面代码
参数: width:编辑器宽 height:编辑器高 toolbars:提供工具按钮列表,取值为列表,如['bold', 'italic'],取值为:mini,normal,full,代表小,一般,全部
imagePath:图片上传的路径,如"images/",实现上传到"/images"文件夹 filePath:附件上传的路径,如"files/",实现上传到"/files"文件夹 实际测试直接保存在 下,并不会拼接从参数设置的路径。
DjangoUeditor 上传图片,视频保存已经帮我们处理好,直接注册就可以使用。
DjangoUeditor 是第三方个人开发,百度编辑器官方没有django版本的。
Ajax应用
1、Ajax
AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
本节课讲解在Django中使用jquery的ajax进行数据交互
jquery框架中提供了$.ajax、$.get、$.post方法,用于进行异步交互。这里我们使用$.post方法演示。
$.get(url, [data], [callback], [type])
$.post(url, [data], [callback], [type])
url:请求地址
data:请求参数,可选。
callback:发送成功时回调函数,可选。
type:返回内容格式,xml, html, script, json, text, _default,可选。
2、案例
案例:模拟用户注册账号密码校验。
1、校验用户名是否存在,用户名是否合法(8-16位字符组成,不能是纯数字,或字母) 2、校验密码是否合法(8-16位字母和数字组成),两次输入的密码是否一致。 3、当用户输入完成失去焦点之后校验,如果输入的是非法数据,提示用户出错。
创建在应用目录下创建一个 register.html 注册模板,写入下面注册表单。
<form method="post" action="/blog/register/" onsubmit="return false;">
<label>帐号:</label><br>
<input id="uname" name="uname">
<span class="name_error" style="color: red;"></span>
<br>
<label>密码:</label><br>
<input id="upwd" name="upwd">
<span class="upwd_error" style="color: red;"></span>
<br>
<label>重复输入密码:</label><br>
<input id="spwd" name="spwd">
<span class="spwd_error" style="color: red;"></span>
<br>
<input type="checkbox" id="protocol" checked="checked"><span>同意用户协议</span>
<span class="protocol_error" style="color: red;" hidden>请同意用户协议</span>
<br>
<input type="submit" value="注册">
</form>
创建视图处理请求注册页面:
def register(request):
return render(request,'personal_blog/register.html')
创建一个视图验证用户名是否可用:
def check_name(request):
name = request.POST.get('uname') # 接收前端传过来的用户名,去数据库中查找,没找到说明是可以注册的。
s_name = User.objects.filter(username=name)
if s_name:
# 返回json数据,0表示可以注册,1表示用户名已存在
return JsonResponse({"success":0})
else:
return JsonResponse({"success":1})
创建一个视图接收用户注册参数并保存数据库:
这里不存数据库,接收到前端传过来的数据即可。
def register_user(request):
info = request.POST
username = info.get('uname')
password = info.get('upwd')
print(username.password)
return JsonResponse({'is_error':0})
配置url:
url(r'^check_name/$', views.check_name),
url(r'^register_user/$', views.register_user),
要是用JQuery 中的post方法需要先将jQuery文件引入到模板中。
用户输入数据校验,并用$.post方法与django后台交互。
<script>
$(function () {
error_check_password = false;
error_check_username = false;
error_check_upwd = false;
error_check_checked = false;
$('#uname').blur(function () {
//账号框输入密码失去焦点时,调用检验账号的方法
check_uname()
});
$('#upwd').blur(function () {
check_upwd() // 调用检验密码的方法
})
$('#spwd').blur(function () {
//当重复输入密码框失去焦点时调用方法判断两次输入是否一直
check_spwd()
});
$('#protocol').click(function () {
check_checked()
});
function check_uname() {
//判断用户名是否存在,是否合法
var username = $('#uname').val();
var len = username.length;
console.log(username);
console.log(isNaN(username));
//用户名符合条件,发送给后台数据库进行对比是否已经注册
if (isNaN(username) && (len > 6 && len < 18)) {
$('#uname').next().hide();
//post方法发送帐号给服务器,进行比对,
//第一个参数是url.第二个参数是发给服务器的post参数,第三个data是回调函数接收服务器返回的数据
$.post('/blog/check_name/', {'uname': username}, function (data) {
//
if (data.success == 0) {
$('#uname').next().html('用户名存在');
$('#uname').next().show();
error_check_username = false
}
else {
error_check_username = true
$('#uname').next().hide()
}
});
} else {
//不满足条件提示用户
$('#uname').next().html('8-16位字符组成,不能是纯数字,或字母');
$('#uname').next().show();
error_check_username = false
}
}
function check_upwd() {
var upwd = $('#upwd').val();
var re = /^[a-z0-9A-Z]{8,18}$/; //正则
if (re.test(upwd) && isNaN(upwd)) {
$('#upwd').next().hide()
error_check_upwd = true
} else {
$('#upwd').next().html('密码为8-16位字母和数字组成')
$('#upwd').next().show()
error_check_upwd = false
}
}
//判断用户两次输入的密码是否正确
function check_spwd() {
//获取两次输入密码
var upwd = $('#upwd').val();
var spwd = $('#spwd').val();
//判断两次输入是否一直
if (upwd != spwd) {
$('#spwd').next().html('两次输入的密码不一致');// 不一致的话显示
$('#spwd').next().show();
error_check_password = false;
}
else {
$('#spwd').next().hide();
error_check_password = true;
}
}
//checked框,判断用户是否勾选协议
function check_checked() {
var start = $('#protocol').prop('checked');
if (start) {
error_check_checked = true;
$('.protocol_error').hide()
}
else {
$('.protocol_error').html('请同意用户协议')
$('.protocol_error').show()
error_check_checked = false;
}
}
//提交注册
$(":submit").click(function () {
//防止用户没有填写直接提交。
check_uname()
check_upwd()
check_checked()
check_spwd()
console.log(error_check_password, error_check_username, error_check_checked, error_check_upwd)
if (error_check_password && error_check_username && error_check_checked && error_check_upwd) {
// 如果全部条件都符合,那么提交表单数据
var username = $('#uname').val();
var upwd = $('#upwd').val();
$.post('/blog/register_user/', {'uname': username, 'upwd': upwd}, function (data) {
//注册成功 加载成功页面,或者重定向到其他页面
//window.href = 'http://www.baidu.com'
$('html').html(data)
})
} else {
return false
}
})
});
</script>
CSRF跨站请求伪造
1、CSRF介绍
在前面课程中使用Post请求的时候,出现403错误,当时处理方式是注释掉中间件的csrf这行。这节课我们将详细讲解什么是csrf,csrf的功能以及作用。
CSRF, Cross Site Request Forgery, 跨站点伪造请求。攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。造成个人信息泄露,与财产安全。
csrf攻击过程:
1.用户打开浏览器,访问A网站,输入用户名和密码请求登录网站A; 2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A; 3.用户未退出网站A之前,在同一浏览器中,打开另外一个网站B;
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以用户的权限处理该请求,导致来自网站B的恶意代码被执行。
防御CSRF攻击: 目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。
Django 防御csrf攻击: Django提供了csrf中间件用于防止CSRF攻击,而且是默认开启的。MIDDLEWARE_CLASSES 中的csrf中间件,csrf中间件只对post请求有效,get请求无效。
1、django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,
把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,
2、在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token
3、在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段,只需要在表单中添加一个{% csrf_token %}标签,
django会自动在模板中生成一个隐藏的表单域,带有后端响应页面时塞进来的随机生成的token值。
4、在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。
如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.
5、在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值
开启了csrf中间件之后,在模板中的form表单添加一个{% csrf_token %}。
如果是用form表单提交的话直接在表单中添加 {% csrf_token %}。
如果是用ajax提交的话,需要携带一个键为 `csrfmiddlewaretoken` 值为 {% csrf_token %} 的参数
示例:
如果在模板中写的 post 提交的话 可以在参数中携带这个参数:
csrfmiddlewaretoken: '{{ csrf_token }}'
$.post('/blog/register_user/', {'uname': username, 'upwd': upwd,csrfmiddlewaretoken: '{{ csrf_token }}'}, function (data) {
//注册成功 加载成功页面,或者重定向到其他页面
//window.href = 'http://www.baidu.com'
$('html').html(data)
在js文件中,可以先获取前端页面中由{% csrf_token %} 生成的隐藏域的input 标签的value值
<input name="csrfmiddlewaretoken" value="lt5VtILNu1q44aVfQEFP2NgiUEtere0bZNnoNVSNjjX1AsfRDE9bvSn7ZmbeYL7Z" type="hidden">
var csrf = $("input[name='csrfmiddlewaretoken']").val();
$.post('/blog/check_name/', {'uname': username,csrfmiddlewaretoken: csrf}, function (data) {
//
}