csrf跨站请求伪造、csrf相关装饰器、auth认证模块、基于django中间件设计项目功能

csrf跨站请求伪造

什么是csrf跨站请求伪造

简单的来说就是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作,由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行

经典例子-钓鱼网站

钓鱼网站:假设是一个跟银行一模一样的网站页面,用户在该页面上转账
账户的钱会减少,但是受益人却不是自己想要转账的那个人

模拟

一台计算机上俩个服务端不同端口启动,钓鱼网站提交地址改为正规网站的地址
真正网站的代码

前端:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h2>这是真正的网站</h2>
<form action="" method="post">
    <p>username:
        <input type="text" name="username">
    </p>
    <p>target_user:
        <input type="text" name="target_user">
    </p>
    <p>money:
        <input type="text" name="money">
    </p>
    <input type="submit">
</form>
</body>
</html>

后端:
from django.shortcuts import render

# Create your views here.
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print(f'{username}给{target_user}转了{money}')
    return render(request, 'transfer.html')

路由:
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('transfer/', views.transfer),
]

钓鱼网站的代码

前端:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h2>这是钓鱼网站</h2>
<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">
        <input type="text" name="target_user" value="小笨蛋" style="display: none">

    </p>
    <p>money:
        <input type="text" name="money">
    </p>
    <input type="submit">
</form>
</body>
</html>

后端:
from django.shortcuts import render

# Create your views here.
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print(f'{username}给{target_user}转了{money}')
    return render(request, 'transfer.html')

路由:
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('transfer/', views.transfer),
]

钓鱼网站的实现效果
image

如何避免这种现象(预防)

image
csrf策略:通过在返回的页面上添加独一无二的标识信息从而区分正规网站和钓鱼网站的请求

如何在django中解决这个问题

image

form表单

<form action="" method="post">
    	{% csrf_token %}
</form>

image

image

ajax

image

方式1:先编写csrf模板语法 然后利用标签查找和值获取 手动添加
      'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()
方式2:直接利用模板语法即可
      'csrfmiddlewaretoken':'{{ csrf_token }}'
方式3:通用方式(js脚本)
      兼容性最好,扩展性最高,前后端分离
方式3的js代码,直接copy就可以
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);
    }
  }
});

csrf相关装饰器

1.当整个网站默认都不校验csrf 但是局部视图函数需要校验 如何处理

2.当整个网站默认都校验csrf 但是局部视图函数不需要校验 如何处理

FBV

from django.views.decorators.csrf import csrf_protect,csrf_exempt
"""
csrf_protect 校验csrf
csrf_exempt  不校验csrf
"""
# @csrf_protect  # 全局不校验的时候,想叫谁校验就给谁安这个
@csrf_exempt  # 全局都校验,只有这个不校验就添加这个
def home(request):
    return HttpResponse('不在原地踏步')

CBV

针对CBV不能直接在方法上添加装饰器 需要借助于专门添加装饰器的方法

from django.utils.decorators import method_decorator

# @method_decorator(csrf_protect, name='post')  # 方式2:指名道姓的添加
class MyHome(views.View):
    @method_decorator(csrf_protect)  # 方式3:影响类中所有的方法
    def dispatch(self, request, *args, **kwargs):
        super(MyHome, self).dispatch(request, *args, **kwargs)
	'''
	当路由匹配成功之后,才会执行dispatch方法,然后它会去找视图函数,所以给dispatch装上就·相当于给所有的方法都装上了装饰器
	'''
    def get(self, request):
        return HttpResponse('Home Get view')

    # @method_decorator(csrf_protect)  # 方式1:指名道姓的添加
    def post(self, request):
        return HttpResponse('Home Post view')
针对csrf_exempt只有方式3有效 针对其他装饰器上述三种方式都有效

auth认证模块

auth模块模块是什么

Auth模块是Django自带的用户认证模块

我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统,django执行数据库迁移命令之后会产生一个auth_user表,该表可以配合auth模块做用户相关的功能:注册 登录 修改密码 注销 ...,该表还是django admin后台管理默认的表

Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点,它内置了强大的用户认证系统———Auth模块

使用admin后台管理

创建了超级管理员

image

管理员登录成功
image

这个管理员功能是非常强大的,可以直接对表进行增删改查、修改数据等,并且页面也挺好看
image

增加表中数据
image

Auth模块方法编写登录、注册功能

auth.authenticate(request, username=username, password=password)返回的是一个对象

# res = auth.authenticate(request, username=username, password=password)
# print(res)
# print(res.username)  # jason
# print(res.password)  # pbkdf2_sha256$150000$wymiPvzXEUIH$vn6XTMN/YWwiGa98WScB/Hs5MfPtB5br+JCM3oWBtwk=
# print(res.pk)  # 2
is_user_obj = auth.authenticate(request, username=username, password=password)
        """
        数据存在的时候返回的是一个数据对象
        数据不存在的时候返回的是一个None
        """

用户登录时候与用户没有登录(request.user)

print(request.user)
"""
    当用户登录成功之后(执行了auth.login) 该方法返回当前登录用户对象(admin)
    当用户没有登录成功(没有执行了auth.login) 该方法返回匿名用户对象(AnonymousUser)
"""

判断当前用户是否登录 (request.user.is_authenticated)

校验用户是否登录

导入模块
from django.contrib import auth

from django.contrib.auth.decorators import login_requiblack

局部校验

校验用户是否登录, 默认跳转登录地址比较复杂, 我们可以自定义跳转登录的地址
image

@login_requiblack(login_url='/login/') 它会自动跳转到login

image

全局校验

在settings.py中配置: LOGIN_URL = '/login/'

注册功能

image

def register(request):
    User.objects.create_user(username='jerry', password=123)  # 创建普通用户
    User.objects.create(username='jerry1', password=123)  # 创建普通用户,密码不能加密
    return HttpResponse('注册功能')

image

def register(request):
    User.objects.create_superuser(username='oscar', password=123, email='oscar@163.com')  # 创建超级管理员
    return HttpResponse('注册功能')

Auth模块常用的方法

导入Auth模块:from django.contrib import auth

1.authenticate()
    校验用户名和密码是否正确
    auth.authenticate(request,username,password)
2.login
    用户登录
    auth.login(request, user_obj)
3.is_authecticated
    判断用户是否登录
    request.user.is_authecticated
4.create_user、create_superuser
    创建用户
    User.object.contrib.auth.models import User
    User.object.create_user(username, password)
    User.object.create_superUser(username, password, email)
5.check_password
    校验密码是否正确
    request.user.check_password(old_password)
6.set_password
    request.user.set_password(new_password)
    request.user.save()  # 必须要保存
7.logout
    auth.logout(request)
8.校验用户登录装饰器
    from django.contrib.auth.decorators import login_requiblack
    跳转局部配置
       login_requiblack(login_url='/login/')
    跳转全局配置
        LOGIN_URL = '/login/'
9.获取登录用户对象
    request.user

auth_user表切换

我们还想使用auth模块的方法并且扩展auth_user表字段

1.models.py
    from django.contrib.auth.models import AbstractUser  # auth_user表序列化的一个类

    class Userinfo(AbstractUser):
        '''扩展auth_user表中没有的字段'''
        phone = models.BigIntegerField()
        desc = models.TextField()
"""
写一个类继承AbstractUser这个类,那么我写的这个类就拥有了AbstractUser这个类里面所有的字段
"""

2.settings.py
    AUTH_USER_MODEL = 'app01.Userinfo'

基于django中间件设计项目功能

"""
模块的导入
	import 句式
	from ... import ... 句式
"""
s1 = 'ffff.bbb.b'
import importlib

res = importlib.import_module(s1)
print(res)
print(res.name)

image

简单的函数式封装

def send_qq(content):
    print('qq消息通知:', content)
def send_wechat(content):
    print('微信消息通知:', content)
def send_msg(content):
    print('短信消息通知:', content)

def send_all(content):
    # send_qq(content)  不想发哪个消息就注释那个函数
    send_wechat(content)
    send_msg(content)

if __name__ == '__main__':
    send_all('所有后来呢')

配置文件插拔是设计

image
qq.py文件

class QQ(object):  # 为了兼容括号内写object
    def __init__(self):
        pass  # 模拟发送qq之前 应该准备的代码环境

    def send(self, content):
        print('qq消息通知:', content)

__init.py__文件

import importlib
import settings

def send_all(content):
    for i in settings.NOTIFY_FUNC_LIST:  # 'day64.notify.qq.QQ'
        module_path, class_str_name = i.rsplit('.', maxsplit=1)  # 'day64.notify.qq'  'QQ'
        module = importlib.import_module(module_path)  # from day64.notify import qq  from day64.notify import weixin  from day64.notify import msg
        class_name = getattr(module, class_str_name)  # 真正的类名
        obj = class_name()
        obj.send(content)

setting.py文件

NOTIFY_FUNC_LIST = [
    'ffff.notify.qq.QQ',
    'ffff.notify.weixin.WeiXin',
    'ffff.notify.msg.Msg',
    # 'ffff.notify.tt.Tt'  # 如果那天不想用了,直接注释掉就可以
]

start.py文件

from ffff import notify

if __name__ == '__main__':
    notify.send_all('国庆过得越痛苦 后面工作越轻松')
posted @ 2022-09-13 21:07  张张包~  阅读(45)  评论(0编辑  收藏  举报