django请求生命周期流程图
djang中间件
中间件是一个用来处理Django的请求和响应的框架级别的钩子,用于在全局范围内改变Django的输入和输出,只要是全局相关的功能都应该考虑使用Django中间件来完成
只要是全局相关的功能你都应该考虑使用django中间件来帮你完成
全局用户身份校验
全局用户访问频率校验
用户访问黑名单
用户访问白名单
中间件在settings配置文件中
from django.middleware.security import SecurityMiddleware # 查看某个中间件代码方式,直接复制上去用from...import... 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支持用户自定义中间件(例)
class SessionMiddleware(MiddlewareMixin): def process_request(self, request): def process_response(self, request, response): class CsrfViewMiddleware(MiddlewareMixin): def process_request(self, request): def process_view(self, request, callback, callback_args, callback_kwargs): def process_response(self, request, response): class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request):
中间件干了什么事?
请求来时由Django中间件默认的七个关口进行校验,当任意一个校验不通过都会返回,当七个默认关口都校验成功后,判断是不是第一次请求,如果不是,会直接去缓存数据库中查找数据,拿到数据后再通过中间件默认七个关口,通过web服务网关接口后返回给客户端浏览器数据,当第一次请求来时会依次通过url,views最后到数据库拿到数据后由视图层直接到中间件通过七个关口校验,当校验成功后,将数据进入缓存数据库一份,再通过web服务网关接口发送到客户端浏览器一份数据
自定义中间件
我们也可以自定义中间件,需要创建一个文件夹下写自定义中间件代码,并且需要将自定义的中间件配置到配置文件中,自定义中间件要继承中间件继承的 MiddlewareMixin
Django允许自定义中间件并且暴露给用户五个自定义的方法
1、process_request
1、中间件是在执行视图函数之前执行的
2、请求来的时候会按照配置文件中注册的中间件从上往下的顺序依次执行每一个中间件里面的process_request方法,如果没有就直接跳过执行下一个
3、如果中间有一个中间件有return HttpResponse ,就不会再执行后面的了,就直接同级别执行process_response后返回数据
# 自定义的中间件 from django.utils.deprecation import MiddlewareMixin class Mymd1(MiddlewareMixin): def process_request(self, request): print('我是第1个process_request') class Mymd2(MiddlewareMixin): def process_request(self, request): print('我是第2个process_request')
# settings中配置 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', 'mymiddlewrae.mymiddle.Mymd1', 'mymiddlewrae.mymiddle.Mymd2', ]
# views中 def index(request): print('index') return HttpResponse('index')
最后运行打印结果
我是第1个process_request
我是第2个process_request
index
2、process_response
响应走的时候会按照配置文件中注册的中间件从下往上依次执行每一个中间件里面的process_response方法,该方法必须要有两个形参,且必须要将形参response返回,如果中间件内部定义了HttpResponse对象,返回的时候就会将返回给用户浏览器的内容替换成自己的
# 自定义的中间件 from django.utils.deprecation import MiddlewareMixin class Mymd1(MiddlewareMixin): def process_request(self, request): print('我是第1个process_request') def process_response(self, request, response): print('我是第1个process_response') return response class Mymd2(MiddlewareMixin): def process_request(self, request): print('我是第2个process_request') def process_response(self, request, response): print('我是第2个process_response') return response
最后返回的结果:
我是第1个process_request
我是第2个process_request
index
我是第2个process_response
我是第1个process_response
3、process_view
路由匹配成功之后,进入视图函数之前触发
4、process_template_response
也必须要有两个参数,response必须要方法,有response的都要返回
视图函数返回的对象中必须要有render属性对应的方法,必须要自定义的render方法
# 视图函数中 def index(request): print('index') def render(): return HttpResponse('我是index中的render方法') obj = HttpResponse('index') obj.render = render return obj
# 自定义中间件 def process_template_response(self,request, response): print('我是第2个process_temlate_response') return response
5、process_exception
当视图函数报错时自动触发
# 中间件中 def process_exception(self,request,exception): print('exception:',exception)
跨站请求伪造csrf
钓鱼网站
本质搭建一个跟正常网站一模一样的页面
用户在该页面上完成转账功能
转账的请求确实是朝着正常网站的服务端提交
唯一不同的在于收款账户人不同
给用户书写form表单 对方账户的input没有name属性
你自己悄悄提前写好了一个具有默认的并且是隐藏的具有name属性的input
模拟钓鱼网站
<form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text"></p> <input type="text" name="target_user" style="display: none" value="jason"> <p>money:<input type="text" name="money"></p> <input type="submit"> </form>
为了防止这种情况发生,我们可以在我们的网站设置csrf_token校验,来防止跨站伪造请求
原理:由token产生随机字符串,每次请求都会新生成不同的,服务器会将需要用户填写数据的(post请求)给她一个随机字符串,下次发送请求的时候会校验是否有那个字符串
form表单如何通过csrf校验
在form表单内任意位置书写代码:
{% csrf_token %}
ajax如何通过csrf校验
在data中校验csrf
1、第一种方式:手动获取
$.ajax({
url: '',
type: 'post',
{#第一种方法:手动获取#}
data: {'username':'shen', 'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},
success:function (data) {
...
}
})
2、第二种方式:利用模板语法
$.ajax({
url: '',
type: 'post',
{#第二种方法:利用模板语法#}
data: {'username':'shen', 'csrfmiddlewaretoken':{{ csrf_token }},
success:function (data) {
...
}
})
3、第三种方式:通用方式引用外部js文件使所有的post请求都要校验(官方提供方法)
外部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); } } });
引用外部js文件后Ajax就可以正常写提交请求,就可以了
<button id="d1">发送Ajax请求</button> {% load static %} <script src="{% static 'mycsrf.js' %}"></script> <script> $('#d1').click(function () { $.ajax({ url: '', type: 'post', {#第三种方法:引用外部js文件,Ajax不要任何操作#} data: {'username':'shen'}, success:function (data) { alert(data) } }) }) </script>
csrf相关装饰器
1、当我们网站引用外部js文件使得整个网站都需要校验csrf时,想让几个视图函数不校验时,使用csrf_exempt 装饰器使得某个视图函数不校验csrf
2、当我们网站整体都不校验csrf的时候,想让某几个视图函数校验csrf,可以使用装饰器 csrf_protect
首先我们需要导入该装饰器
from django.views.decorators.csrf import csrf_exempt, csrf_protect
1、在FBV中
直接在视图函数上装饰@csrf_protect和@csrf_exempt
# @csrf_exempt # 不校验csrf @csrf_protect # 校验csrf def home(request): if request.method == 'POST': username = request.POST.get('username') print(username) return render(request, 'mycsrf.html')
2、在CBV中
首先需要导入csrf_exempt,csrf_protect和method_decorator
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views import View from django.utils.decorators import method_decorator
1、csrf_protect 校验csrf
第一种方式:直接在类中的方法使用 @method_decorator(csrf_protect)
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views import View from django.utils.decorators import method_decorator class Myhome(View): # 第一种方式,直接在类中方法使用 @method_decorator(csrf_protect) def get(self,request): return HttpResponse('get')
第二种方式:给类加装饰器,指名道姓的给类中的某个方法
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views import View from django.utils.decorators import method_decorator # 第二种方法,指名道姓给类中post方法装 @method_decorator(csrf_protect, name='post') class Myhome(View): def get(self,request): return HttpResponse('get') def post(self, request): return HttpResponse('post')
第三种方式:给类中的dispatch方法装,使所有的方法都获得csrf校验
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views import View from django.utils.decorators import method_decorator class Myhome(View): # 第三种方式,给类中的所有方法都装 @method_decorator(csrf_protect) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self,request): return HttpResponse('get') def post(self, request): return HttpResponse('post')
2、csrf_exempt 不校验csrf
只能给类中的dispatch方法装,使得所有的方法都获得,或者给类装指明是给dispatch的也可以
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views import View from django.utils.decorators import method_decorator # 给类中所有的方法都装,使得都不校验csrf,与直接给dispatch装一样的 @method_decorator(csrf_exempt, name='dispatch') class Myhome(View): # 给类中的所有方法都装 @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self,request): return HttpResponse('get') def post(self, request): return HttpResponse('post')
auth模块
Django作为一个完美主义者的终极框架,它内置了强大的用户认证系统--auth,执行数据库迁移的那两条命令时,即使我们没有建表,它默认帮我们创建auth_user 表来存储用户数据。
django用户相关的自带的功能模块 auth_user表
自定义Auth表单
在modles.py
创建表单模型可以对AbstractUser
进行继承。因为我们可以从源码中看出来 Auth 自带的auth_user表继承AbstractUser
类,所有我们可以同样继承它,然后对于他的内容进行自定义
AbstractUser
自带的字段
username :账号
first_name:姓
last_name:名
email:邮箱
is_staff : 用户是否拥有网站的管理权限.
is_active: 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录。
date_joined:创建日期
自定义表
1.类的继承
from django.contrib.auth.models import User,AbstractUser class Userinfo(AbstractUser): #扩展字段 phone = models.BigIntegerField() #扩展的字段尽量不要与原先表中字段冲突
2.配置文件settings.py
AUTH_USER_MODEL = '应用名.表名' ''' 配置后django会将Userinfo表来替换默认自带的auth_user表 '''
创建超级用户
我们可以在pycharm中使用导航栏中的Tools里的run manage.py Task 中输入createsuperuser
注册相关:
create_user()
auth 提供的一个创建新用户的方法,需要提供必要参数(username、password)等。
注意:根据当前User提供必要字段,外加username, password,如果字段不全,会注册失败,但不会报错!!!
用法:
from django.contrib.auth.models import User user = User.objects.create_user(username='用户名',password='密码',...)
create_superuser()
auth 提供的一个创建新的超级用户的方法,需要提供必要参数(username、password、email)等 。(根据当前User提供必要字段,外加username, password)
用法:
from django.contrib.auth.models import User user = User.objects.create_superuser(username='用户名',password='密码',email='邮箱',...)
示例:
from django.contrib import auth from django.contrib.auth.models import User def register(request): if request.method =='POST': username = request.POST.get('username') password = request.POST.get('password') # 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') #创建超级用户 return render(request,'register.html')
登录相关
authenticate()
提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数
登录成功返回的是用户对象,错误返回None。
user = auth.authenticate(request,username=username,password=password)
login()
保存用户登录状态,会自动在后端为该用户生成相关session数据
#若用户登录成功,拿到user对象 if user: #保存用户登录状态 auth.login(request,user) ''' 只要执行了这一句话,之后在任意可以获取到request对象的地方 都可以通过request.user获取到当前登录的用户对象 '''
is_authenticated()
用来判断当前请求是否通过了认证,返回布尔值
解释:user为request的属性,request.user拿到用户对象,也可以.models类,request.user相当于userinfo.object.filter.first拿到的一样,也支持跨表查询
request.user.is_authenticated()
校验用户是否登录装饰器 @login_required
方式一:局部配置
@login_required(login_url='/login') #加参数 若未登录就跳转到指定的登录页面 def xxx(request): return HttpResponse('xxx页面')
方式二:全局配置
views.py
@login_required def xxx(request): return HttpResponse('xxx页面')
在settings.py中加入以下代码
LOGIN_URL = '/login/'
注意:若两种方法都有,则先执行方式一的
登录综合示例:
def login(request): if request.method =='POST': username = request.POST.get('username') password = request.POST.get('password') user = auth.authenticate(request,username=username,password=password) #返回值user是一个对象 # print(user) # print(user.username) # print(user.password) ''' 用户名密码正确返回的是用户对象 错误返回None ''' if user: #保存用户登录状态 auth.login(request,user) ''' 只要执行了这一句话,之后在任意可以获取到request对象的地方 都可以通过request.user获取到当前登录的用户对象 ''' return HttpResponse('登录成功') return render(request,'login.html') def get_user(request): print(request.user) ''' 用户登录成功之后,request.user拿到的就是用户对象 user 没有登录,获取到的是匿名用户 AnonymousUser ''' print(request.user.is_authenticated()) #返回的是布尔值,确认用户是否登录 return HttpResponse('get_user') # 校验用户是否登录装饰器 from django.contrib.auth.decorators import login_required @login_required(login_url='/login') #加参数 跳转到指定的登录页面 # @login_required def xxx(request): return HttpResponse('xxx页面')
密码相关
check_password(password)
校验密码是否正确,密码正确返回True,否则返回False
is_right = request.user.check_password(old_password)
set_password(new_password)
设置新密码,传新密码当作参数,设置完一定要调用用户对象的save方法。
request.user.set_password(new_password) request.user.save() #这步一定要做,保存到数据库
修改密码综合示例:
@login_required def set_password(request): if request.method =='POST': old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') #1 先校验旧密码是否正确 is_right = request.user.check_password(old_password) print(is_right) #2 修改新密码 if is_right: request.user.set_password(new_password) request.user.save() #这步一定要做,保存到数据库 return render(request,'set_password.html')
注销相关
logout()
注销登录用户
auth.logout(request)