CBV装饰器
写一个装饰器在session上
def login_auth(func): def inner(request,*args,**kwargs): if request.session.get('is_login'): return func(request,*args,**kwargs) else: return redirect('/login/') return inner
三种方法:
第一种就是直接在里面给方法加上装饰器,方法上面加,不要用原生的装饰器,用的话,只能改参数,那样的话不通用
第二种就是在类的上面加装饰器,name=‘方法’指向该方法
from django.utils.decoration import method_decorator
# @method_decorator(login_auth,name='get') # 第二种 name参数必须指定 class MyHome(View):
# @method_decorator(login_auth) # 第一种 def get(self,request): return HttpResponse('get') def post(self,request): return HttpResponse('post')
第三种:利用dispatch来装饰所有的方法
class MyHome(View): @method_decorator(login_auth) # 第三种 get和post都会被装饰 def dispatch(self, request, *args, **kwargs): super().dispatch(request,*args,**kwargs) def get(self,request): return HttpResponse('get') def post(self,request): return HttpResponse('post')
中间件
那么,我们还是先来看一下Django的请求生命周期
从上面的图中我们可以发现,中间件在这其中起到了很重要的作用。
它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
在settings文件里面就有这样的中间件配置
django默认有七个中间件,但是django暴露给用户可以自定义中间件并且里面可以写五种方法
ps:
1.请求来的时候会依次执行每一个中间件里面的process_request方法(如果没有直接通过)
2.响应走的时候会依次执行每一个中间件里面的process_response方法(如果没有直接通过)
django中间件能够帮我实现 网站全局的身份验证,黑名单,白名单,访问频率限制,反爬相关
》》》:django用来帮你全局相关的功能校验
自定义中间件
新建一个任意名字的文件夹,里面新建一个任意名字py文件
from django.utils.deprecation import MiddlewareMixin
插件类:
process_request:请求来的时候从上往下依次执行每一个中间件里面的process_request
process_response :响应走的时候会从下往上依次执行每一个中间件里面的process_response方法
process_view:路由匹配成功执行视图之前自动触发(从上往下依次执行)
process_exception:当视图函数报错了,自动触发(从下往上依次执行)
process_template_response:视图函数返回的对象有一个render()方法
(或者表明该对象是一个TemplateResponse对象或等价方法)(从下往上依次执行)
自定义中间件
from django.utils.deprecation import MiddlewareMixin class MyMiddleWare(MiddlewareMixin): def process_request(self, request): print('我是第一个自定义的中间件的process_request!') # 写一个路由与视图,启动项目,查看打印结果。 # 再写一个自定义中间件 class MyMiddleWare(MiddlewareMixin): def process_request(self, request): print('我是第二个自定义的中间件的process_request!') # 诠释中间件的执行顺序 # 在两个中间件中添加process_response方法。研究中间件消息进出的顺序 class MyMiddleWare(MiddlewareMixin): def process_request(self, request): print('我是第一个自定义的中间件的process_request!') def process_response(self,request,response): print('我是第一个自定义的中间件的process_response') return response # 再来研究process_request中直接返回HttpReponse对象,会怎么走接下来的process_response # process_view,process_exception,process_template_response
先写一个简单的post请求,复现报错信息
钓鱼网站:银行转账的路径,你是否也可以拿到,然后你做一个跟银行一模一样的页面,也超银行的借口提交数据,当用户在钓鱼网站输入对方账户名和转账金额之后,点击发送。其实内部是将对方账户换成了钓鱼网站的造假人员的账户。造成你转账转错账户的情况
开两个django项目,模拟转账的现象
如何区分钓鱼网站和正经网站?在正经网站返回页面的时候,在form表单中偷偷塞一个特殊的字符串,后端记下该页面对应的字符串的值,等用户发post请求来的时候,我先去校验特殊的字符串是否匹配
如何去写这个特殊的字符串呢?模版语法有一个固定的写法{% csrf_token %},必须写在form表单内
浏览器查看改标签的值,并且每次都在刷新。再来演示刚刚转账的示例
ajax中如何设置csrf_token
# 只想给某个视图韩式加上csrf校验 from django.views.decorators.csrf import csrf_exempt,csrf_protect # 局部禁用 @csrf_exempt def index(request): pass # 局部使用 @csrf_protect def login(request): pass
转账钓鱼案例
def transfer(request): if request.method == 'POST': username = request.POST.get("username") money = request.POST.get('money') others = request.POST.get('others') print('%s 给 %s 转了 %s'%(username,others,money)) return render(request,'transfer.html')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> </head> <body> <h1>正经的网站</h1> <form action="/index3/" method="post"> {# {% csrf_token %}#} <p>username:<input type="text" name="username"></p> <p>money:<input type="text" name="money"></p> <p>others:<input type="text" name="others"></p> <input type="submit"> </form> <button>ajax</button> <script> $('button').click(function () { $.ajax({ url:'', type:'post', data:{'name':'khan','csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()}, success:function (data) { console.log(data) } }) }) </script> </body> </html>
另起一个项目,钓鱼页面与原始页面类似
通过像原网站发送请求,钓鱼网站的一个input框就用户替换了,这时候钱自然就流向了非法分子,有了{% csrf_token %},那么服务端就不在接受钓鱼网站的post请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> </head> <body> <h1>钓鱼网站</h1> <form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username:<input type="text" name="username"></p> <p>money:<input type="text" name="money"></p> <p>others:<input type="text"></p> <input type="text" name="others" value="khan" style="display:none"> <input type="submit"> </form> </body> </html>
执行数据库迁移的那两条命令时,即使我们没有建表,也会出现autho_user表,我们一起来看一下怎么操作该表。
from django.contrib import auth from django.contrib.auth.models import User def auth_login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # models.User.objects.filter(username=username,password=password).first() user_obj = auth.authenticate(request,username=username,password=password) if user_obj: # 记录用户状态 # request.session['name'] = 'khan' auth.login(request,user_obj) # 一旦记录了,可以在任意的地方通过request.user获取到当前登录对象 return HttpResponse('ok') return render(request,'auth_login.html') def auth_index(request): print(request.user.is_authenticated()) # 判断当前用户是否已经登录 print(request.user,type(request.user)) # 获取当前登录用户对象 return HttpResponse('ok') def auth_logout(request): auth.logout(request) # request.session.flush() return HttpResponse('ok') def auth_register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user_obj = auth.authenticate(request,username=username) if user_obj: return HttpResponse('当前用户已存在') # models.User.objects.create(username=username,password=password) # User.objects.create(username=username,password=password) # 不能再用create创建 # User.objects.create_user(username=username,password=password) # 创建普通用户 User.objects.create_superuser(username=username,password=password,email='123@163.com') # 创建超级用户 return render(request,'auth_register.html') def auth_password(request): print(request.user.password) is_res = request.user.check_password('khan123') # 校验密码是否一致 if is_res: request.user.set_password('666') # 设置新密码 request.user.save() # 修改密码必须save保存 不然无效 return HttpResponse('ok')
from django.contrib.auth.decorators import login_required @login_required(login_url='/login/',redirect_field_name='old')
# 没登陆会跳转到login页面,并且后面会拼接上你上一次想访问的页面路径/login/?next=/test/,可以通过参数修改next键名 def my_view(request): pass
如果我所有的视图函数都需要装饰并跳转到login页面,那么我需要写好多份。
其实可以去settings里面去配置
# 可以在配置文件中指定auth校验登陆不合法统一跳转到某个路径 LOGIN_URL = '/login/' # 既可以局部配置,也可以全局配置
第一种:可以建立一对一关系的模型表
from django.contrib.auth.model import User class UserDetail(models.Models): phone = models.CharField(max_length=11) user = models.OnoToOneField(to=User)
第二种:面向对象继承
from django.contrib.auth.models import User,AbstractUser class UserInfo(AbstractUser): phone = models.CharField(max_length=32) # 需要在配置文件中,指定我不再使用默认的auth_user表而是使用我自己创建的Userinfo表 AUTH_USER_MODEL = "app名.models里面对应的模型表名"