django中间件是http请求在经过wsgiref网关后在抵达路由系统之前所经过的一系列对数据的校验和处理。
因为所有的http请求都会经由中间件处理,所以中间件有关的绝大数是全局相关的功能,例如黑名单、白名单、全局用户身份校验、全局用户访问频率校验。
1.django自带的中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
django支持自定义中间件
from django.utils.deprecation import MiddlewareMixin
class Mymiddleware(MiddlewareMixin):
def process_request(self,request): # 请求过来时执行的函数
pass
def process_response(self,request,response): # 响应返回时
pass
def process_view(self,request) # 匹配成功执行视图函数前触发
def process_template_response # 返回的reponse对象必须有render属性
def exception # 视图函数异常出错时触发
2.跨站请求伪造csrf
Cross-site request forgery,概括的来说服务器收到的请求的确发自用户的浏览器,但却不知道请求本身是否是用户自愿发出的。
# 假如一家银行用以运行转账操作的URL地址如下:
http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
# 那么,一个恶意攻击者可以在另一个网站上放置如下代码:
<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">
在用户不知情的情况下会向银行发送转账请求,却不是用户的自愿行为。所以需要对服务器的安全性验证进行升级,csrf由此而来。
服务器会给每个有post或get请求的网页随机生成一串字符串csrf_token,当用户发送post提交时必须携带上这个随机字符串,不然在django中间件时就会将本次http请求拦截。
form表单携带csrf校验
<!-- 在form表单内写入 -->
{% csrf_token %}
ajax携带csrf校验
// 第一种方式 自己手动获取
{#data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},#}
// 第二种方式 利用模板语法
{#data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
// 第三种 通用方式 引入外部js文件 官网提供的方式
{% load static %}
<script src="{% static 'myset.js' %}"></script>
data:{'username':'yyh'}
myset.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 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);
}
}
});
3.csrf相关装饰器
当全局时csrf校验而有些视图函数不需要csrf校验时
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
@csrf_exempt
def home(request):
return HttpResponse('home page')
当全局不设置csrf校验而有些视图函数需要csrf校验时
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
@csrf_protect
def home(request):
return HttpResponse('home page')
当视图函数基于CBV时
# @method_decorator(csrf_protect,name='post') # 第二种指名道姓的给类中某个方法装
# @method_decorator(csrf_exempt,name='post') # csrf_exempt 第二种方式不行
@method_decorator(csrf_exempt,name='dispatch') # 可以!!!
class MyHome(View): # APIView
# @method_decorator(csrf_protect) # 第三种 类中所有的方法都装
# @method_decorator(csrf_exempt) # csrf_exempt 第三种方式可以
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self,request):
return HttpResponse('get')
# @method_decorator(csrf_protect) # 第一种方式
# @method_decorator(csrf_exempt) # csrf_exempt 第一种方式不行
def post(self,request):
return HttpResponse('post')
详见django官方文档 https://docs.djangoproject.com/en/1.11/ref/csrf/
4.auth模块
from django.utils.safestring import mark_safe
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
1.创建用户
from django.contrib.auth.models import User
def register(request):
if request.method == 'GET':
return render(request, 'register.html')
username = request.POST.get('username')
password = request.POST.get('password')
obj = User.objects.create_user(username=username,password=password)
print(obj)
return HttpResponse('注册成功')
2.校验用户名登录
from django.contrib.auth.models import User
def login(request):
if request.method=='GET':
return render(request,'login.html')
username = request.POST.get('username')
password = request.POST.get('password')
user_obj = auth.authenticate(username=username,password=password)
# 校验成功返回用户对象,否则为None
if user_obj:
return HttpResponse('登录成功')
return HttpResponse('登录失败')
3.保存用户登录状态
from django.contrib.auth.models import User
def login(request):
if request.method=='GET':
return render(request,'login.html')
username = request.POST.get('username')
password = request.POST.get('password')
user_obj = auth.authenticate(username=username,password=password)
# 校验成功返回用户对象,否则为None
if user_obj:
# 此后都可通过request.user获取用户对象(保存了session值)
auth.login(request,user_obj)
return HttpResponse('登录成功')
return HttpResponse('登录失败')
4.如何判断当前用户是否登录以及如何获取当前登录用户
def home(request):
# 未登录时为 AnonymousUser
print(request.user)
# 判断用户是否已经登录,返回True或False
print(request.user.is_authenticated())
if request.user.is_authenticated():
return HttpResponse('已登录')
else:
return HttpResponse('未登录')
5.校验用户是否登录
from django.contrib.auth.decorators import login_required
# 局部配置装饰器校验失败跳转url
@login_required(login_url='/login/')
def index(request):
# request.user获取用户记录对象
info = str(request.user.last_login) + str(request.user.password) + str(request.user.username)
return HttpResponse(info)
# 全局配置
settings.py
-----------------------------------------
LOGIN_URL = '/login/'
-----------------------------------------
views.py
-----------------------------------------
@login_required
def index(request):
# request.user获取用户记录对象
info = str(request.user.last_login) + str(request.user.password) + str(request.user.username)
return HttpResponse(info)
6.用户修改密码
@login_required
def set_password(request):
user_obj = request.user
if request.method == 'GET':
return render(request,'set_password.html',locals())
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
# check_password
is_true = user_obj.check_password(old_password)
if is_true:
# set_password
user_obj.set_password(new_password)
# 一定要save
user_obj.save()
return HttpResponse('修改密码成功')
else:
return HttpResponse('修改密码失败')
7.用户注销
@login_required
def logout(request):
res = auth.logout(request)
print(res)
return HttpResponse('注销成功')
5.扩展auth_user表
django为我们提供了默认了auth_user表,有时候该表不一定能满足我们的需求,所以需要我们手动来扩展表
# 类的继承
models.py
--------------------------------------------
from django.contrib.auth.models import User, AbstractUser
class Userinfo(AbstractUser):
phone = models.BigIntegerField(null=True)
avatar = models.FileField(null=True)
# 拓展的字段不要与原先表中的字段冲突
settings.py
-------------------------------------------------
AUTH_USER_MODEL = 'app01.Userinfo'