Django-Part6——中间件与Auth模块 [TOC]
Django中间件 中间件简介
django自带七个中间件,并且支持程序员自定义中间件
只要是涉及到全局相关的功能都可以使用中间件完成
全局用户身份、权限校验
…
django中间件是django的门户
请求需要先经过中间件才能到达django后端
响应也需要经过中间件才能发送出去
自定义中间件
中间件可以在项目名或者应用名下任意名称的文件夹中定义
自定义中间件类必须继承于MiddlewareMixin类
需要将类的路径以字符串的形式注册到配置文件中才能生效
中间件将暴露五个可以自定义的方法,按需求重写
process_request(self, request)
:
请求来的时候需要经过每一个中间件里面的process_request方法
顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
如果中间件里面没有定义该方法,则跳过
如果该方法返回了HttpResponse对象,则请求将不再继续往后执行,而是从同级别的process_reponse向上原路返回
flask中即使中途返回数据,但依然经过所有中间件
process_request方法可以用来做全局相关的所有限制功能
process_response(self, request, response)
:
响应走的时候需要经过每一个中间件里面的process_response方法
顺序是按照配置文件中注册了的中间件从下往上依次经过
如果中间件里面没有定义该方法,则跳过
该方法必须返回一个HttpResponse对象,默认返回的就是形参response
process_view(self, request, view_func, view_args, view_kwargs)
:
触发时间在路由匹配成功之后,执行视图函数之前
顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
process_template_response(self, request, response)
:
返回的HttpResponse对象有render属性的时候才会触发
顺序是按照配置文件中注册了的中间件从下往上依次经过
process_exception(self, request, exception)
:
当视图函数中出现异常的情况下触发
顺序是按照配置文件中注册了的中间件从下往上依次经过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from django.utils.deprecation import MiddlewareMixinclass MD1 (MiddlewareMixin ): def process_request (self, request ): print("MD1里面的 process_request" ) def process_response (self, request, response ): print("MD1里面的 process_response" ) return response def process_view (self, request, view_func, view_args, view_kwargs ): print("-" * 80 ) print("MD1 中的process_view" ) print(view_func, view_func.__name__) def process_exception (self, request, exception ): print(exception) print("MD1 中的process_exception" ) return HttpResponse(str (exception)) def process_template_response (self, request, response ): print("MD1 中的process_template_response" ) return response
csrf跨站请求伪造 使用cookie完成csrf校验
为了确保请求来源是本服务器发送出去的
为每一次发出去的页面提供唯一标识,如果校验标识失败则直接拒绝(403 forbbiden)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <script src="get请求url" ></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]); 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 ) { return (/^(GET|HEAD|OPTIONS|TRACE)$/ .test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings ) { if (!csrfSafeMethod(settings.type) && !this .crossDomain) { xhr.setRequestHeader("X-CSRFToken" , csrftoken); } } });
如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。
如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。
这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。
1 2 3 4 5 django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def login (request ): pass
csrf相关装饰器
@csrf_protect:需要校验
@csrf_exempt:忽视校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from django.views.decorators.csrf import csrf_protect, csrf_exemptfrom django.utils.decorators import method_decoratordef transfer (request ): if request.method == 'POST' : username = request.POST.get('username' ) target_user = request.POST.get('target_user' ) money = request.POST.get('money' ) print('%s给%s转了%s元' %(username,target_user,money)) return render(request,'transfer.html' ) from django.views import View@method_decorator(csrf_exempt, name='dispatch' ) class MyCsrfToken (View ): def dispatch (self, request, *args, **kwargs ): return super (MyCsrfToken, self).dispatch(request, *args, **kwargs) def get (self,request ): return HttpResponse('get' ) def post (self,request ): return HttpResponse('post' )
跨域请求
对于前后端分离的报错:has been blocked by CORS policy: NO 'Access-Control-Allow-Origin' header is present on the reauested resource.
同源策略
浏览器的同源策略:浏览器发现ip或端口是不一样的,就会认为存在风险,会进行拦截。
推荐的方法是使用Nginx进行反向代理。
添加响应头解决跨域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from django.utils.deprecation import MiddlewareMixinclass MyCors (MiddlewareMixin ): def process_response (self, request, response ): response["Access-Control-Allow-Origin" ] = "*" if request.method == "OPTIONS" : response["Access-Control-Allow-Headers" ] = "Content-Type" response["Access-Control-Allow-Methods" ] = "DELETE, PUT, POST" return response MIDDLEWARE = [ ... 'app01.middlewares.Mycors.MyCors' , ]
配置文件与反射 importlib模块 使用importlib,以字符串形式导入模块
1 2 3 4 5 6 7 from myfile import bprint(b) import importlibres = 'myfile.b' ret = importlib.import_module(res) print(ret)
实例展示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import settingsimport importlibdef send_all (content ): for path_str in settings.NOTIFY_LIST: module_path,class_name = path_str.rsplit('.' , maxsplit=1 ) module = importlib.import_module(module_path) cls = getattr (module, class_name) obj = cls() obj.send(content)
Auth模块
使用auth模块要用就用全套
Django的admin路由对应的就是就是auth_user表,必须是管理员用户才能进入。
创建超级用户(管理员):python3 manage.py createsuperuser
常用方法
auth.authenticate(request, username, password)
:根据用户名在表中查询加密后的密码,并将请求中的密码加密比对是否正确。返回用户对象,不匹配则返回None
auth.login(request, user_obj)
:保存用户状态,建立session。执行该方法后,可以在任何地方通过request.user
获取到当前登陆的用户对象
request.user
:
request.user.is_authenticated()
:判断当前用户是否登陆
@login_required
:校验登录装饰器
局部配置:@login_required(login_url='/login/')
全局配置:settings.py -> LOGIN_URL = '/login/'
request.user.check_password(old_password)
:将请求中的密码加密,并比对原密码
request.user.set_password(new_password)
:修改用户对象的密码
request.user.save()
:将修改密码后的用户对象写入数据库
auth.logout(request)
:注销,清除双方session
User.objects.create_user(username=username,password=password)
:注册用户。注意不能使用create
方法,该方法不对密码进行加密。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 def login (request ): if request.method == 'POST' : username = request.POST.get('username' ) password = request.POST.get('password' ) user_obj = auth.authenticate(request, username=username, password=password) print(user_obj) print(user_obj.username) print(user_obj.password) if user_obj: auth.login(request, user_obj) return redirect('/home/' ) return render(request, 'login.html' ) @login_required(login_url='/login/' ) def home (request ): print(request.user) print(request.user.is_authenticated()) return HttpResponse('home' ) @login_required def set_password (request ): if request.method == 'POST' : old_password = request.POST.get('old_password' ) new_password = request.POST.get('new_password' ) confirm_password = request.POST.get('confirm_password' ) if new_password == confirm_password and request.user.check_password(old_password): request.user.set_password(new_password) request.user.save() return redirect('/login/' ) return render(request, 'set_password.html' , locals ()) @login_required def logout (request ): auth.logout(request) return redirect('/login/' ) def register (request ): if request.method == 'POST' : username = request.POST.get('username' ) password = request.POST.get('password' ) User.objects.create_user(username=username, password=password) return render(request, 'register.html' )
扩展auth_user表
面向对象继承,自由增加字段,对象可以直接访问字段
如果继承了AbstractUser
,那么在执行数据库迁移命令的时候auth_user
表就不会再创建出来了,而是替换成定义的子类
auth模块的功能还是照常使用
前提:
在继承之前没有执行过数据库迁移命令、auth_user没有被创建
继承的类里面不要覆盖AbstractUser
里面的字段名,只扩展额外字段
需要在配置文件中添加AUTH_USER_MODEL = 'app01.UserInfo'
1 2 class UserInfo (AbstractUser ): phone = models.BigIntegerField()
BBS实战 表设计
用户表(AbstractUser)
phone:电话号码
avatar:用户头像
create_time:创建时间
一对一:个人站点表
个人站点表
site_name:站点名称
site_title:站点标题
site_theme:站点样式
文章标签表
文章分类表
文章表
点赞点踩表(记录哪个用户给哪篇文章点了赞还是点了踩)
user:ForeignKey(to=”User”)
article:ForeignKey(to=”Article”)
is_up:BooleanField()
文章评论表
user:ForeignKey(to=”User”)
article:ForeignKey(to=”Article”)
content:CharField()
comment_time:DateField()
parent:ForeignKey(to=”self/Comment”,null=True)
根评论子评论,评论当前发布的内容,根评论与子评论是一对多的关系