目录标题
一:csrf跨站请求伪造
1.前戏
钓鱼网站:搭建一个和正常网站一模一样的网页,只是后端提交数据的时候,不会按照你输入的用户桩长,而是将钱转到钓鱼网站自己默认设置好的账号中去,导致自己的转账不知去向
钓鱼网站的原理:搭建两个相同的前端页面,然后后端开设两个不同的端口,其中钓鱼网站的前端提交的地址设置为正确网站的地址
2.什么是csrf跨站请求伪造?
- 网站在给用户返回一个具有提交数据功能的页面的时候,会给这个页面加上一个唯一标识,当这个页面朝后端发送post请求的时候,
- 后端会自动校验这一标识,如果标识不对,则直接拒绝(403frobbiden),如果标识正确,则正常执行执行
3.form表单中的csrf操作:
- 方法一:直接在前端页面的form表单中添加{% csrf_token %}
<h1>我是一个正儿八经的网站</h1>
<form action="">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>target_name:<input type="text" name="target_name"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
</form>
</body>
4.ajax中的csrf操作
-
方法一:利用标签查找获取页面上随机字符串
-
方法二:利用模板语法提供的快捷方式
-
方法三:直接拷贝js代码,并应用到自己的html页面上
- 1.创建静态文件夹static
- 2.在文件夹中创建js文件夹
- 3.在js文件夹中创建mysetup.js文件,将配置的代码粘贴到里面
- 4.在settings.py中添加静态文件配置
STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static') ]
- 5.在项目文件中导入该js文件即可
{% load static %} <script src="{% static 'js/mysetup.js' %}"></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]); // 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); } } });
<script>
$('#d1').click( function () {
$.ajax({
url:'',
type:'post',
// 第一种方式:利用标签查找获取页面上随机字符串
{#data: {"username": 'jason', "csrfmiddlewaretoken": $('[name=csrfmiddlewaretoken]').val()},#}
// 第二种方式:利用模板语法提供的快捷方式
{#data: {"username": 'jason', "csrfmiddlewaretoken": {{csrf_token}},#}
// 第三种方式:直接拷贝js代码,并应用到自己的html页面上
success: function () {
}
})
})
</script>
二:csrf相关装饰器
1.前提
1.当整个网站默认都不校验csrf 但是局部视图函数需要校验 如何处理
2.当整个网站默认都校验csrf 但是局部视图函数不需要校验 如何处理
2.FBV中的装饰器
- 装饰器1:csrf_protect 校验csrf
- 装饰器2:csrf_exempt 不校验csrf
from django.views.decorators.csrf import csrf_protect,csrf_exempt
@csrf_exempt
def home(request):
return render(request, 'home.html')
@csrf_protect
def login(request):
return render(request, 'login.html')
3.CBV中的装饰器
- 1.csrf_protect
from django.utils.decorators import method_decorator
from django import views
# 方式二:指名道姓的添加
@method_decorator(csrf_protect, name='post')
class Myindex(views.View):
# 方式三: 直接影响类中的所有方法
def dispatch(self, request, *args, **kwargs):
super(Myindex, self).dispatch(request, *args, **kwargs)
# 方式1:直接指名道姓的添加
@method_decorator(csrf_protect)
def get(self, request):
return HttpResponse('get页面')
def post(self, request):
return HttpResponse('post页面')
- 2.csrf_exempt:只有方式3有效 针对其他装饰器上述三种方式都有效
三:auth认证模块
1.auth模块中的常见方法
1.创建用户:
- 创建普通用户:密码是不被加密的
- 创建超级管理员:必须添加一个email参数
from django.contrib.auth.models import User
User.object.create_user(username, password) # 创建普通用户,密码是没有被加密的
User.object.create_superuser(username, password,email) # 创建超级管理员用户
2.校验用户名和密码
- authenticate方法
from django.contrib import auth
auth.authenticate(request,username,password)
3.用户登录
auth.login(request, user_obj)
4.判断用户是否登录
request.user.is_authenticated
5.获取登录用户对象
request.user
6.校验用户登录装饰器
- 局部配置
- 全局配置:配置到settings.py文件中
from django.contrib.auth.decorators import login_required
# 局部配置
login_required(login_url='/login/')
# 全局配置
LOGIN_URL = '/login/'
7.校验密码是否正确
request.user.check_password(old_password)
8.修改密码
- 切记:修改密码之后一定要保存
request.user.set_password(new_password)
request.user.save()
9.注销登录
auth.logout(request)
10.练习
- urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 登录
url(r'^login/', views.login),
# 登录之后的页面
url(r'^home/', views.home),
# 修改密码的页面
url(r'^set_password/', views.set_password),
# 注销账号
url(r'^logout/', views.logout),
# 注册用户
url(r'^register/', views.register),
]
- views.py
from django.contrib import auth
from django.shortcuts import render, redirect, HttpResponse
# Create your views here.
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)
# 1.自动查找auth_user标签
# 2.自动给密码加密并比对
# 3.必须同时传用户名和密码
# 保存用户状态
if user_obj:
auth.login(request, user_obj)
# 只要执行了该方法,你就可以在任何地方通过request.user获取到当前登录的用户对象
return redirect('/home/')
return render(request, 'login.html')
from django.contrib.auth.decorators import login_required
# @login_required # # 全局配置
# @login_required(login_url='/login/') # 用户没登录的情况下,跳转到指定的页面 局部配置
def home(request):
"""用户登录之后才可以访问的页面"""
print(request.user)
print(request.user.is_authenticated()) # 判断当前用户是否登录
return HttpResponse('ok')
# 设置密码
@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:
if 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/')
from django.contrib.auth.models import User
# 注册用户
@login_required
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)
User.objects.create_superuser(username=username, password=password, email='123@qq.com')
return render(request, 'register.html')
<form action="" method="post">
<h1>登录</h1>
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>password:<input type="text" name="password"></p>
<p><input type="submit" value="登录"></p>
</form>
<form action="" method="post">
<h1>修改密码</h1>
{% csrf_token %}
<p>username:<input type="text" name="username" disabled value="{{ request.user.username }}"></p>
<p>old_password:<input type="text" name="old_password"></p>
<p>new_password:<input type="text" name="new_password"></p>
<p>confirm_password:<input type="text" name="confirm_password"></p>
<input type="submit" value="确认">
</form>
<form action="" method="post">
<h1>注册</h1>
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>password:<input type="text" name="password"></p>
<p><input type="submit" value="注册"></p>
</form>
四:auth_user表切换
- 第一步:models.py中
from django.contrib.auth.models import AbstractUser
class Userinfo(AbstractUser):
'''扩展auth_user表中没有的字段'''
phone = models.BigIntegerField()
desc = models.TextField()
- 第二步:settings.py中
AUTH_USER_MODEL = 'app01.Userinfo'
总结:
- 1.如果继承了AbstractUser,那么在执行数据库迁移命令的时候,auth_user表就不会再被创建出来
- 2.UserInfo表会创建,并且它里面包含auth_user表中所有的字段,并且还可以增加自己独有的字段
- 3.在执行数据库迁移命令之前,不能存在已经创建好的auth_user表,如果存在,则直接换库
- 4.新表中只能创建auth_user表中没有的字段
- 5.必须在配置文件中声明:AUTH_USER_MODEL = ‘app01.UserInfo’
五:基于django中间件设计项目功能
第一步:先创建一个notify文件夹,在该文件夹中可以创建py文件,eg:wechat.py, qq.py, email.py,__ init__.py
__ init__.py文件
import settings
import importlib
def send_all(content):
for path_str in settings.NOTIFY_LIST:
moudel_path, class_name = path_str.rsplit('.', maxsplit=1)
# 1.利用字符串导入模块
module = importlib.import_module(moudel_path)
# 2.利用反射获取类名
cls = getattr(module, class_name)
# 3.生成类对象
obj = cls()
# 4.利用鸭子类型直接调用send方法
obj.send(content)
email.py文件
class Email(object):
def __init__(self):
pass # 发送微信需要做的前期准备
def send(self, content):
print('邮箱通知:%s' % content)
qq.py文件
class QQ(object):
def __init__(self):
pass # 发送微信需要做的前期准备
def send(self, content):
print('QQ通知:%s' % content)
wechat.py文件
class Wechat(object):
def __init__(self):
pass # 发送微信需要做的前期准备
def send(self, content):
print('微信通知:%s' % content)
第二步:在项目根目录下还需要建两个py文件,分别是:start.py和settings.py
start.py文件
import notify
notify.send_all('下课了')
settings.py
NOTIFY_LIST = [
'notify.email.Email',
'notify.qq.QQ',
'notify.wechat.Wechat'
]