Django之csrf中间件及auth模块使用
一、基于配置文件的编程思想
反射
通过字符串操作类和函数
1. importlib 模块
importlib模块可以通过字符串取出其对应的模块。
import importlib
res = 'lib.bbb'
# 利用字符串的形式导入模块
md = importlib.import_module(res)
# from lib import bbb md相当于bbb
print(md) # 该模块字符串最小单位只能到文件名
2. 配置文件
当我们导入一个模块的时候,我们其实是在导入这个模块文件夹中的__init__.py
文件。
该文件中可以导入配置文件中的配置信息。
我们在配置文件中提前将导入其他模块的具体路径写成字符串的形式,保存成配置信息。
然后在__init__.py
文件中通过importlib模块将导入的配置文件的中的配置信息字符串解析成真正的模块路径进行导入。
这样我们就可以在配置文件中输入字符串来进行模块的导入啦!而且如果我们有一些模块及功能不需要用到时,我们直接将配置文件中的路径字符串注释掉就行了。
二、跨站请求伪造(csrf)
1.csrf简介以及由来
Cross—Site Request Forgery:跨站请求伪造
有些钓鱼网站会写成与正规网站一样的界面,诱导用户使用并提交数据。当钓鱼网站接收到用户本来想发送到正规网站的数据后,钓鱼网站将这组信息进行一些修改,将其关键信息改成自己想要的数据,再把这组修改后的网站发送给正规网站进行处理。
这样钓鱼网站就达到了窃取用户信息并从中牟利的目的。这样就会使得用户与正规网站遭受损失。
为了防止这样的情况,正规网站对接受的数据做了一些处理。规定本网站只会处理自己本页面提交的post请求。
这样就可以保证从钓鱼网站发过来的跨站请求失效。
而csrf就是用来进行这样的操作的中间件。通过这个中间件,后端在向前端发送HTML页面时,会在form表单里添加一个具有name属性的input框,并且value是用户登录的真实账户,然后将这个标签隐藏。
这样用户在提交数据时,后端就可以校验你是否有这个隐藏标签,如果有,就说明你是真实用户。如果没有,就说明是其他网站发送的伪造请求。
这样就可以做到保证用户权益的目的了。
2.Django中的csrf中间件如何使用
csrf中间件在配置文件中已经写好,如果不将该中间件注释,它会自动帮后端朝前端发送HTML文件时,加入csrf_token。
这时前端再想向后端发送数据,就必须要携带csrf_token校验参数,使前端数据通过后端的csrf校验。
2.1 普通form表单提交数据
前端如何携带csrf_token:
{# 你只需要在表单中写一个{% csrf_token %} #}
<form action="" method="post">
{% csrf_token %}
{# csrf_token必须写在form表单里面 #}
<p>username:<input type="text" name="username"></p>
<p>target_account:<input type="text" name="target_user"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
</form>
2.2 ajax提交数据
方式1:较为繁琐
先在页面任意的位置上书写{% csrf_token %};
然后在发送ajax请求的时候通过标签查找获取随机字符串,添加到data自定义对象即可。
<script>
$('#d1').click(function () {
$.ajax({
url:'',
type:'post',
// 第一种
data:{
'username':'jason',
'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()
},
success:function (data) {
alert(data)
}
})
})
</script>
方式2:较为简单
<script>
$('#d1').click(function () {
$.ajax({
url:'',
type:'post',
// 第二种
data:{
'username':'jason',
'csrfmiddlewaretoken':'{{ csrf_token }}' {# 要加一下引号 #}
}, {# 要加一下引号 #}
success:function (data) {
alert(data)
}
})
})
</script>
方式3:官网提供文件(最通用的一种方式)
直接新建js文件拷贝代码,导入即可;
你不需要做任何的csrf相关的代码书写
你不需要做任何的csrf相关的代码书写
<script src="/static/setup.js"></script>
<script>
$('#d1').click(function () {
$.ajax({
url:'',
type:'post',
// 第三种 利用脚本文件 啥也不用写
data:{'username':'jason'},
success:function (data) {
alert(data)
}
})
})
</script>
3.csrf相关的装饰器
3.1 csrf_exempt
如果中间件设置了需要全局校验csrf,那么该装饰器可以让函数不进行校验。
# @csrf_exempt # 不校验 csrf
def index(request):
return HttpResponse('index') # 不需要也可以交互
3.2 csrf_protect
如果csrf中间件被注释掉了,设置了全局不校验csrf,那么该函数可以
@csrf_protect # 校验
def login(request):
return HttpResponse('login')
3.3 如何在CBV的函数中使用上面两个函数
- csrf_exempt这个装饰器只能给dispatch装才能生效;
- csrf_protect任何方式都可以,和普通的装饰器装饰CBV一致。
# @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法
@method_decorator(csrf_exempt,name='dispatch') # 可以
class MyIndex(views.View):
# @method_decorator(csrf_exempt) # 可以
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self,request):
return render(request,'transfer.html')
# @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法
def post(self,request):
return HttpResponse('OK')
# @method_decorator(csrf_protect,name='post') # 可以
class MyIndex(views.View):
@method_decorator(csrf_protect) # 可以
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self,request):
return render(request,'transfer.html')
# @method_decorator(csrf_protect) # 可以
def post(self,request):
return HttpResponse('OK')
三、Django auth管理
Auth模块是Django自带的用户认证模块。
有django帮我们做登录认证,很多东西都不用自己写了,真的很开心呢!
用户数据会储存在数据迁移时,Django自动帮我们创建的auth_user表中。
1. auth模块常用方法
1.1 创建用户
想要使用auth创建用户需要先从django.contrib.auth.models
导入user
模块:
from django.contrib.auth.models import User
# 不可用 密码不是加密的
# 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')
# 源码
def create_user(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
1.2 校验用户名和密码
想要使用auth校验用户名和密码需要先从django.contrib
导入auth
模块:
from django.contrib import auth
user_obj = auth.authenticate(request,username=username,password=password)
# 必须传用户名和密码两个参数 缺一不可
如果该用户未找到则user_obj返回none。
1.3 保存用户登录状态
使用auth.login(request,user_obj)
保存用户登录状态,自动在user_session表中记录。
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)
if user_obj:
# print(user_obj)
# print(type(user_obj))
# 保存用户登录状态,在session表中保存用户
# 相当于request.session['user'] = 'name'
# 且只要执行了这一句话,
# 之后你可以在任意位置通过request.user获取到当前登录用户对象
auth.login(request,user_obj)
return HttpResponse('登陆成功!')
return render(request,'login.html')
1.4 判断当前用户是否登录
使用request.user.is_authenticated()
判断用户是否登录,返回布尔值,True/False。
def check_login(request):
if request.user.is_authenticated():
return HttpResponse('login')
else:
return HttpResponse('not login')
1.5 校验原密码是否正确
通过request.user.check_password(旧密码)
来校验用户是否输入了正确的密码,返回值为布尔值True/False。
@login_required # 局部登录跳转页面配置
def check_password(request):
user_obj = request.user # 获取已经登陆的用户
if request.method == 'POST':
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
# 校验原来的密码是否正确 返回布尔
is_right = user_obj.check_password(old_password)
if is_right:
user_obj.set_password(new_password)
user_obj.save()
return HttpResponse('修改成功!')
return render(request,'change_password.html')
1.6 修改密码
通过request.user.set_password(新密码)
来修改用户的密码,修改完成后需要执行request.user.save()
来将修改后的数据写入数据库。
不然数据只是在缓存中修改了一下,没有真正写入数据库。
@login_required # 局部登录跳转页面配置
def check_password(request):
user_obj = request.user # 获取已经登陆的用户
if request.method == 'POST':
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
# 校验原来的密码是否正确 返回布尔
is_right = user_obj.check_password(old_password)
if is_right:
# 修改用户密码,并保存
user_obj.set_password(new_password)
user_obj.save()
return HttpResponse('修改成功!')
return render(request,'change_password.html')
1.7 注销
直接通过auth.logout(request)
方法注销用户。
@login_required
def logout(request):
auth.logout(request)
return HttpResponse('用户已注销!')
1.8 校验用户是否登录装饰器
1.首先需要导入登录认证装饰器模块函数
from django.contrib.auth.decorators import login_required
2.然后可以设置未登录时跳转到登录页面的url
局部配置
@login_required(login_url='/login/')
def func(request):
pass
全局配置 (settings配置文件中 直接配置)
# settings中配置
LOGIN_URL = '/login/'
# views
@login_required
def func(request):
pass
注意:
如果全局配置了,局部也配置,将以局部的login_url
为准。
2. 扩展auth_user表中的字段
2.1 利用一对一建立外键关系表
另写一张表用来存放需要添加的字段,用一对一的关系外键绑定到auth_user表上。
绑定之前需要把from django.contrib.auth.models import User
自动创建的表导过来。
# models
from django.contrib.auth.models import User
class UserDetail(models.Model):
phone = models.BigIntegerField()
user = models.OneToOneField(to='User')
2.2 继承Django提供的auth类
在创建所有表之前直接修改本应该有的auth_user表。
通过继承User表的父类,自己重写新的User类。
继承之前同样需要将User父类from django.contrib.auth.models import AbstractUser
导过来。
# models
from django.contrib.auth.models import AbstractUser
class Userinfo(AbstractUser):
phone = models.BigIntegerField()
register_time = models.DateField(auto_now_add=True)
注意:
自己重写类后,需要在配置文件中配置一下,不然系统不知道你要自己重写表了。
写完之后,之前所有的auth模块功能也全都可以通过你新写的表使用。
# settings
AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名