ajax与安全认证机制CSRF
目录
ajax
ajax是什么?
AJAX是一种客户端Web开发技术
特点是异步请求,局部刷新;这一特点给用户的感受是在不知不觉中完成请求和响应过程
ajax怎么用?
先引入jquery
第一步绑定事件
第2步利用jquery获取用户内容
第3步 利用ajax 义字典形式数据提交到url对应的地址 进行路由匹配,执行相应的视图函数 返会逻辑结果
第4步ajax中success键对应的匿名函数 形参res接受到,之前视图函数的返回值 进行匿名函数逻辑判断 比如利用js添加内容等等
{% csrf_token %}---必加安全验证
用户名: <input type="text" id="username">
密码: <input type="password" id="password">
<button id="sub">提交</button>
<span class="error"></span>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$('#sub').click(function () {
var uname = $('#username').val();
var pwd = $('#password').val();
var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值
$.ajax({
url:'{% url "login" %}',
type:'post',
data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
success:function (res) {---ret是后端给的
if (res === '1'){
location.href = '/home/'; // 重定向http://127.0.0.1:8000/home/
}else{
$('.error').text('用户名密码错误!');
}
}
})
})
AJAX常见应用情景
搜索引擎根据用户输入的关键字,自动提示检索关键字。
还有一个很重要的应用场景就是注册时候的用户名的查重。
安全认证机制CSRF
安全认证机制CSRF是什么?
django中的安全认证机制CSRF?
如何解决django中CSRF?
安全认证机制CSRF是什么?
详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。
django中的安全认证机制CSRF?
django项目中默认对POST请求进行了csrf认证,只需要在模板提交数据的代码块中加入模板标签{{% vsrf_token %}}即可,(不需要注销seetings.py配置文件MIDDLEWARE列表中的'django.middleware.csrf.CsrfViewMiddleware',),模板页面在渲染时会自动在相应位置渲染成隐藏的input标签:<input type="hidden" name="csrfmiddlewaretoken" value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">每次都是随机的
表现情况
在之前我们提交数据必须`注了配置项MIDDLEWARE里面的django.middleware.csrf.CsrfViewMiddleware',否则403`为什么要有这个 安全认证机制csrftoken
如何解决django中CSRF?
两种情况
当form表单提交数据时
当ajax提交数据时
代码详解如下
当form表单提交数据时
form表单里面要有这个就行
<form action="" method="post">
{% csrf_token %}
用户名: <input type="text" name="username">
密码: <input type="password" name="password">
<input type="submit">
</form>
当ajax提交数据时
{% csrf_token %}会生成隐藏的input标签 name是csrfmiddlewaretoken固定的values值是随机的 为什么要这个input标签 官方源码解释
官方文档中说到,检验token时,只比较secret是否和 cookie中的secret值一样,而不是比较整个token。
token字符串的前32位是 salt(盐), 后面是加密后的 token(令牌), 通过salt能解密出唯一的secret。
django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。
同样也不难解释,为什么ajax请求时,需要从cookie中拿取token添加到请求头中。
第一种方式
{% csrf_token %}---必加
用户名: <input type="text" id="username">
密码: <input type="password" id="password">
<button id="sub">提交</button>
<span class="error"></span>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$('#sub').click(function () {
var uname = $('#username').val();
var pwd = $('#password').val();
var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值
$.ajax({
url:'{% url "login" %}',
type:'post',
data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
success:function (res) {---ret是后端给的
if (res === '1'){
location.href = '/home/'; // http://127.0.0.1:8000/home/
}else{
$('.error').text('用户名密码错误!');
}
}
})
})
第2种方式
{% csrf_token %}---必加
用户名: <input type="text" id="username">
密码: <input type="password" id="password">
<button id="sub">提交</button>
<span class="error"></span>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$('#sub').click(function () {
var uname = $('#username').val();
var pwd = $('#password').val();
var csrf = '{{ csrf_token }}'; ---第2种方式固定写法 csrf_token会获得csrf_token中键随机值
$.ajax({
url:'{% url "login" %}',
type:'post',
data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
success:function (res) {---ret是后端给的
if (res === '1'){
location.href = '/home/'; // http://127.0.0.1:8000/home/
}else{
$('.error').text('用户名密码错误!');
}
}
})
})
第3种
注意:需要引入一个jquery.cookie.js插件。
<script src="{% static 'js/jquery.cookie.js' %}"></script>
{% csrf_token %}---必加
用户名: <input type="text" id="username">
密码: <input type="password" id="password">
<button id="sub">提交</button>
<span class="error"></span>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$('#sub').click(function () {
var uname = $('#username').val();
var pwd = $('#password').val();
var csrf = '{{ csrf_token }}'; ---第2种方式固定写法 csrf_token会获得csrf_token中键随机值
$.ajax({
url:'{% url "login" %}',
type:'post',
data:{username:uname,password:pwd},
headers:{"X-CSRFToken":$.cookie('csrftoken')}--固定写法值是cookie
success:function (res) {---ret是后端给的
if (res === '1'){
location.href = '/home/'; // http://127.0.0.1:8000/home/
}else{
$('.error').text('用户名密码错误!');
}
}
})
})
django文件上传
form表单上传文件
ajax上传文件
form表单上传文件
理论
form表单提交的文件在request.FILES里面 如果不改消息格式则request.FILES里面为空
修改消息格式 在form表单中加入 enctype="multipart/form-data"
获得文件对象(句柄)file_obj = request.FILES.get('file') 括号里面的file就是 后端<input type="file" name="file">标签对应的name值
获得文件名称 就用文件对象.name 及就是 file_obj.name
有了文件对象(句柄) 和原文件名字就可以写入文件了
步骤
第1步 修改消息格式 在form表单中加入 enctype="multipart/form-data"
第2步 后端代码获得文件对象 与名字就可以写人文件了
实现代码如下
前端代码
前端代码
<form action="" method="post" enctype="multipart/form-data">--到定义
{% csrf_token %}
用户名: <input type="text" name="username">
密码: <input type="password" name="password">
上传文件: <input type="file" name="file">
<input type="submit">
</form>
后端代码
def upload(request):
if request.method == 'GET':
print(settings.BASE_DIR) #/static/
return render(request,'upload.html')
else:
#print(request.POST)--获取的是对象 里面有文件名
#print(request.FILES) --对象 文件对象 存在内存中 如果不改消息格式里面没东西
uname = request.POST.get('username')
pwd = request.POST.get('password')
file_obj = request.FILES.get('file')---获取文件句柄对象
print(file_obj.name) #开班典礼.pptx
with open(file_obj.name,'wb') as f:
# for i in file_obj: --怕文件中没有换行 会爆内存
# f.write(i)
for chunk in file_obj.chunks():---一般用这种一次读取65536个字节
f.write(chunk)
return HttpResponse('ok')
扩展内容
怎么修改chunks()默认读取子节?
配置文件修改chunks()
from django.conf import settings与from 项目名 import settings的区别?
from django.conf import settings是内置的另一个配置文件,外层settings没有的内层里面可能有
from 项目名 import settings是当前项目外层的配置文件 外层有的就覆盖了内层的
用法上from django.conf import settings更厉害 外层配配置文件没有的可能能在内层配置找到
ajax上传文件
第1步绑定点击事件
第2步 点击 触发事件 执行匿名函数 函数里面逻辑利用js获得想要的结果
var file = $('[type=file]')[0].files[0];--获取上传对象
第3步 创建FormData对象 借助他上传
第4步 将匿名函数或的的想要的结果append 键值对的形式加进去
第5步 ajax上传加入固定写法 叫django不要处理代码示例
processData:false,--固定写法 叫django不要处理
contentType:false,--固定写法 叫django不要处理
后端代码不变
多加一个参数multiple
后端也要改
{% csrf_token %}
用户名: <input type="text" name="username">
密码: <input type="password" name="password">
上传文件: <input type="file" name="file" >
<input type="submit">
</form>
$('#sub').click(function () {
var formdata = new FormData();---创建FormData对象借助他上传
var uname = $('#username').val();
var pwd = $('#password').val();
var file = $('[type=file]')[0].files[0];--获取对象
formdata.append('username',uname); 封装到对象里面
formdata.append('password',pwd);
formdata.append('file',file);
$.ajax({
url:'{% url "upload" %}',
type:'post',
data:formdata,---发这个
processData:false,--固定写法 叫django不要处理
contentType:false,--固定写法 叫django不要处理
success:function (res) {---ret是后端给的
if (res === '1'){
location.href = '/home/'; // 重定向http://127.0.0.1:8000/home/
}else{
$('.error').text('用户名密码错误!');
}
}
})
})
后端
def upload(request):
if request.method == 'GET':
print(settings.BASE_DIR) #/static/
return render(request,'upload.html')
else:
#print(request.POST)--获取的是对象 里面有文件名
#print(request.FILES) --对象 文件对象 存在内存中 如果不改消息格式里面没东西
uname = request.POST.get('username')
pwd = request.POST.get('password')
file_obj = request.FILES.get('file')---获取文件句柄对象
print(file_obj.name) #开班典礼.pptx
with open(file_obj.name,'wb') as f:
# for i in file_obj: --怕文件中没有换行 会爆内存
# f.write(i)
for chunk in file_obj.chunks():---一般用这种一次读取65536个字节
f.write(chunk)
return HttpResponse('ok')
传输json字典数据
一般我们后端返回给前端的数据都是字典形式 所有要用到JSON 转化为特殊格式,便于解析回去
3种方式
第一种方式利用 json模块 low逼写法 后端要反序列化
import json
et_data_json = json.dumps(要转化的字典dic,ensure_ascii=False)
var res = JSON.parse(res); -- 反序列化变成js自定义对象
第2种方式 高大尚写法
import json
ret_data_json = json.dumps(要转化的字典dic,ensure_ascii=False)
return HttpResponse(ret_data_json,content_type='application/json')
会自动转化为json数据 前端就不要反序列化了
第3种写法 最终版
from django.http import JsonResponse
return JsonResponse(字典)
--------------第一种
html页面
{% csrf_token %}---必加
用户名: <input type="text" id="username">
密码: <input type="password" id="password">
<button id="sub">提交</button>
<span class="error"></span>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$('#sub').click(function () {
var uname = $('#username').val();
var pwd = $('#password').val();
var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值
$.ajax({
url:'{% url "login" %}',
type:'post',
data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
success:function (res) {
var res = JSON.parse(res); -- 反序列化变成js自定义对象
if (res.status === 1000){
// $('.error').text('登录成功');
location.href = '/home/'; // http://127.0.0.1:8000/home/
}else{
$('.error').text(res.msg);
}
}
})
})
后端代码
def jsontest(request):
if request.method == 'GET':
return render(request,'jsontest.html')
else:
username = request.POST.get('username')
pwd = request.POST.get('password')
ret_data = {'status':None,'msg':None}
if username == 'chao' and pwd == '123':
ret_data['status'] = 1000 # 假设1000状态码登录成功自己设置的
ret_data['msg'] = '登录成功'
else:
ret_data['status'] = 1001 # 假设1001状态码登录失败自己设置的
ret_data['msg'] = '登录失败'
et_data_json = json.dumps(ret_data,ensure_ascii=False)
return HttpResponse(et_data_json)
--------------第2种方式
后端代码
def jsontest(request):
if request.method == 'GET':
return render(request,'jsontest.html')
else:
username = request.POST.get('username')
pwd = request.POST.get('password')
ret_data = {'status':None,'msg':None}
if username == 'chao' and pwd == '123':
ret_data['status'] = 1000 # 假设1000状态码登录成功自己设置的
ret_data['msg'] = '登录成功'
else:
ret_data['status'] = 1001 # 假设1001状态码登录失败自己设置的
ret_data['msg'] = '登录失败'
return HttpResponse(ret_data_json,content_type='application/json')
会自动转化为json数据 前端就不要反序列化了
html页面
{% csrf_token %}---必加
用户名: <input type="text" id="username">
密码: <input type="password" id="password">
<button id="sub">提交</button>
<span class="error"></span>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$('#sub').click(function () {
var uname = $('#username').val();
var pwd = $('#password').val();
var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值
$.ajax({
url:'{% url "login" %}',
type:'post',
data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
success:function (res) {
//不要这个了 var res = JSON.parse(res); -- 反序列化变成js自定义对象
if (res.status === 1000){
// $('.error').text('登录成功');
location.href = '/home/'; // http://127.0.0.1:8000/home/
}else{
$('.error').text(res.msg);
}
}
})
})
第3种方式
from django.http import JsonResponse
后端代码
def jsontest(request):
if request.method == 'GET':
return render(request,'jsontest.html')
else:
username = request.POST.get('username')
pwd = request.POST.get('password')
ret_data = {'status':None,'msg':None}
if username == 'chao' and pwd == '123':
ret_data['status'] = 1000 # 假设1000状态码登录成功自己设置的
ret_data['msg'] = '登录成功'
else:
ret_data['status'] = 1001 # 假设1001状态码登录失败自己设置的
ret_data['msg'] = '登录失败'
return JsonResponse(ret_data)
会自动转化为json数据 前端就不要反序列化了
html页面
{% csrf_token %}---必加
用户名: <input type="text" id="username">
密码: <input type="password" id="password">
<button id="sub">提交</button>
<span class="error"></span>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$('#sub').click(function () {
var uname = $('#username').val();
var pwd = $('#password').val();
var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值
$.ajax({
url:'{% url "login" %}',
type:'post',
data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
success:function (res) {
//不要这个了 var res = JSON.parse(res); -- 反序列化变成js自定义对象
if (res.status === 1000){
// $('.error').text('登录成功');
location.href = '/home/'; // http://127.0.0.1:8000/home/
}else{
$('.error').text(res.msg);
}
}
})
})
非学,无以致疑;非问,无以广识