django 之csrf、auth模块及settings源码、插拔式设计

目录

  • 基于django中间件拷贝思想
  • 跨站请求伪造简介
  • 跨站请求伪造解决思路
    • 方式1:form表单发post请求解决方法
    • 方式2:ajax发post请求解决方法
  • csrf相关的两个装饰器
  • csrf装饰器在CBV上的特例
  • django settings源码
  • auth模块简介
  • auth创建用户
  • auth扩展表
  • 基于django settings配置文件实现插拔式设计

csrf:Cross Site Request Forgery protection

基于django中间件拷贝思想

# start.py
import notify
notify.send_all('小宝贝们!快要放假啦,你们都去哪里玩呀?')

# settings.py
NOTIFY_LIST = [
    'notify.email.Email',
    'notify.msg.Msg',
    'notify.wechat.WeChat'
]

# notify文件夹下(start.py和.settings.py和notify文件夹在同一目录下)
# __init__.py
# coding:utf8

import settings
import importlib
def send_all(content):
    for path in settings.NOTIFY_LIST:
        file_path,cls_name = path.rsplit('.',1)
        file_name = importlib.import_module(file_path)
        cls = getattr(file_name,cls_name)
        obj = cls()
        obj.send(content)

# email.py
# coding:utf8
class Email:
    def __init__(self):
        pass
    def send(self,content):
        print('邮件通知:%s'%content)

# msg.py
# coding:utf8
class Msg:
    def __init__(self):
        pass
    def send(self,content):
        print('短信通知:%s'%content)

# wechat.py
# coding:utf8
class WeChat:
    def __init__(self):
        pass
    def send(self,content):
        print('微信通知:%s'%content)

跨站请求伪造简介

钓鱼网站
            你自己写一个跟中国银行正规网站一模一样的页面
            用户输入用户名 密码 对方账户  转账金额提交
            请求确实是朝中国银行的接口发送的 钱也扣了
            但是对方账户变了 变成了钓鱼网站自己提前设置好的账户

            如何实现
                你在写form表单的时候 让用户填写的对方账户input并没有name属性
                而是你自己在内部偷偷隐藏了一个具有name属性的input框
                并且value值是你自己的账户 然后将该标签隐藏了

            模拟该现象的产生
                创建两个django项目

ps:

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('%s给%s转账了%s元钱!'%(username,target_user,money))
    return render(request,'transfer.html')
<!--正经网站-->
<body>
<p>这是legal_site</p>
<form action="" method="post">
    <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>
</body>
<!--钓鱼网站-->

<body>
<p>这是fake_site</p>
    <!--不指定路径,默认往当前网站提交,所以这里要伪造成往真正的网站站点提交,真网站后台获取的是钓鱼网站隐藏的用户名的input框指定的value属性值(默认值,就是骗子账户),那么之后转账的处理功能可定就是往这个骗子账户转钱了-->
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>username:<input type="text" name="username"></p>
    <!--吧用户转给真正目标用户的input框的name属性删除了,自己再多加一个input框添加上name属性,并设上value(默认值)属性(骗子的账户名),并把该标签隐藏,后端获取name属性的username,就是骗子设置value属性的骗子账户-->
    <p>target_account:<input type="text" >
        <input type="text" name="target_user"  value="zhang" style="display:none">
    </p>
    <p>money:<input type="text" name ="money"></p>
    <input type="submit">
</form>
</body>

跨站请求伪造解决思路


如何解决该问题:
    只处理本网站发送的post请求

如何识别如何判断当前请求是否是本网张发出的

    解决:
   网站在返回给用户一个form表单的时候 会自动在该表单隐藏一个input框
这个框的value是一个随机字符串 但是网站能够记住给每一个浏览器发送的随机字符串,并与自己后台保存的随机字符串比较,两者一致则通过,否则就返回不存在(403),不再对该请求做处理;

方式1:form表单发post请求解决方法

在写form表单的时候 只需要在表单中写一个{% csrf_token %}
<form action="" method="post">
    {% csrf_token %}
    <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>

下图是网站在form表单隐藏的随机字符串,那么属性就是settings文件中的django自带的七个中间件中的一个,以后用form表单发送post请求时,表单内写上{% csrf_token %},就不需要注释settings中的中间件django.middleware.csrf.CsrfViewMiddleware了。

方式2:ajax发post请求解决方法

ajax方式2:

较为繁琐

? 先在页面任意的位置上书写{% csrf_token %},然后在发送ajax请求的时候 通过标签查找获取随机字符串添加到data自定义对象即可。

<!--ajax方式1:-->

{% csrf_token %}
<button id="d1">发送ajax</button>
<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:{'username':'zhang','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},
            success:function (data) {

            }
        })
    })
</script>

ajax方式2:

较为简单

在ajax方式1的基础上,将ajax内部参数data改成如下方式。

<!--ajax方式2:-->
data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}

ajax方式3:

最通用的一种方式

官网提供的文件,直接兴建js文件拷贝代码 导入即可
你不需要做任何的csrf相关的代码书写

第一步:先在settings文件中配置static路径:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static')
]

第二部:建立static文件夹,自内部建一个js文件,将如下代码拷进去;

// setup.js文件

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);
    }
  }
});

第三步:在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的)

<!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="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
    <button id="d1">发送ajax</button>

<!--导入该js文件-->
<script src="/static/setup.js"></script>

<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:{'username':'zhang'},
            success:function (data) {
                alert(data)
            }
        })
    })
</script>
</body>
</html>

scrf详见===>Djagno官方文档中关于CSRF的内容](https://docs.djangoproject.com/en/1.11/ref/csrf/)

csrf相关的两个装饰器

将settings中csrf中间件注释掉,代表所有的视图函数在浏览器的请求都不校验;

注释掉csrf中间件的情况下,如果想对某个post请求的视图函数校验,可以加装饰器@csrf_protect

没注释掉csrf中间件的情况下,如果朝某个视图函数发送post请求,排除校验,可以加装饰器@csrf_exempt

这两个装饰器跟settings中的csrf中间配置没有关系,它内部源码自动调用csrf。

    from django.shortcuts import render,HttpResponse
    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    # @csrf_exempt  # 不校验 csrf
    def index(request):
        return HttpResponse('index')

    @csrf_protect  # 校验
    def login(request):
        return HttpResponse('login')            

csrf装饰器在CBV上的特例

csrf_exempt和csrf_protect装饰器在CBV上的不同:

? csrf_exempt:这个装饰器只能给dispatch装才能生效

? csrf_protect:全都可以,跟普通的装饰器装饰CBV一致

ps:csrf_exempt

# @method_decorator(csrf_exempt,name='post')  # csrf_exempt不支持该方法
@method_decorator(csrf_exempt,name='dispatch')  # 意思是给其类内部名为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')       

ps:csrf_protect

# @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 settings源码

django有两个配置文件
一个是暴露给用户可以配置的

? 一个是内部全局的(用户配置了就用用户的 用户没有配就用自己的)

from django.conf import global_settings
from django.conf import settings

obj.name = 'zhang'  # 全局
obj.name = 'li'  # 局部

先加载全局配置 给对象设置
然后在加载局部配置  再给对象设置
一旦有重复的项  后者覆盖前者
class Settings(object):
    def __init__(self, settings_module):  # 项目名:settings
        # update this dict from global settings (but only for ALL_CAPS settings)
        for setting in dir(global_settings):  # 获取django全局配置文件中所有的变量名
            if setting.isupper():  # 只有大写的才能通过
                setattr(self, setting, getattr(global_settings, setting))
                # self就是settings
        # store the settings module in case someone later cares
        self.SETTINGS_MODULE = settings_module

        mod = importlib.import_module(self.SETTINGS_MODULE)
        # from 项目名 import settings 导入暴露给用户的配置文件
        # mod就是模块settings

        tuple_settings = (
            "INSTALLED_APPS",
            "TEMPLATE_DIRS",
            "LOCALE_PATHS",
        )
        self._explicit_settings = set()
        for setting in dir(mod):  # 获取暴露给用户的配置文件中所有的变量名
            if setting.isupper():  # 变量名必须是大写
                setting_value = getattr(mod, setting)  # 利用反射获取大写变量名对应的值

                if (setting in tuple_settings and
                        not isinstance(setting_value, (list, tuple))):
                    raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting)
                setattr(self, setting, setting_value)  #
                self._explicit_settings.add(setting)

        if not self.SECRET_KEY:
            raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")

        if hasattr(time, 'tzset') and self.TIME_ZONE:
            # When we can, attempt to validate the timezone. If we can't find
            # this file, no check happens and it's harmless.
            zoneinfo_root = '/usr/share/zoneinfo'
            if (os.path.exists(zoneinfo_root) and not
                    os.path.exists(os.path.join(zoneinfo_root, *(self.TIME_ZONE.split('/'))))):
                raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE)
            # Move the time zone info into os.environ. See ticket #2315 for why
            # we don't do this unconditionally (breaks Windows).
            os.environ['TZ'] = self.TIME_ZONE
            time.tzset()

    def is_overridden(self, setting):
        return setting in self._explicit_settings

    def __repr__(self):
        return '<%(cls)s "%(settings_module)s">' % {
            'cls': self.__class__.__name__,
            'settings_module': self.SETTINGS_MODULE,
        }

auth模块简介

Auth模块使Django自带的用户认证模块,它内置了强大的用户认证系统--auth,默认使用auth-user表来存储用户数据;这样开发人员就不用手写django-ORM部分的登录、注册,用户认证、注销、修改密码等功能,以及创建user表的麻烦。

首先,执行数据库迁移命令,找到auth-user这张表,内部有:password、last_login、is_superuser first_name last_name email is_staff is_active date_joined username 字段。

然后创建按超级用户:

? python3 manage.py createsuperuser # cmd执行该命令

? 需要输入创建超级用户的用户名和密码以及再次创建密码

auth创建用户

1.创建用户

User.objects.create(username=username,password=password)  # 不可用  密码不是加密的
User.objects.create_user(username=username,password=password)  # 创建普通用户    密码自动加密
 # 创建超级用户   需要邮箱数据,密码自动加密
User.objects.create_superuser(username=username,password=password,email='[email protected]') 
from django.contrib.auth.models import User
def register(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # User.objects.create(username=username,password=password)
        # User.objects.create_user(username=username,password=password)
        User.objects.create_superuser(username=username,password=password,email='[email protected]')
        return HttpResponse('注册成功!!')

    return render(request,'register.html')

2.校验用户名和密码是否正确

from django.contrib import auth
# 必须传用户名和密码两个参数缺一不能
user_obj = auth.authenticate(request,username=username,password=password)   

3.保存用户登录状态

auth.login(request,user_obj)
# 只要这句话执行了 后面在任意位置 只要你能拿到request你就可以通过request.user获取到当前登录的用户对象
def login_dunc(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 校验当前用户名和密码是否正确
        # models.User.objects.filter(username=username,password=password)
        # res = User.objects.filter(username=username,password=password)  # 密码无法校验,前端传的是明文,后端存的是密文
        # print(res)
        res = auth.authenticate(request,username=username,password=password)  # 自动加密密码 然后去数据库校验
        # print(res)  # 数据对象
        # print(res.username)
        # print(res.password)  # 密码是密文
        if res:
            # 保存用户登录状态
            # request.session['user'] = 'zhang'
            auth.login(request,res)
            """
            只要执行了这一句话 之后你可以在任意位置
            通过request.user获取到当前登录用户对象
            """
            return HttpResponse('ok')
    return render(request,'login_dunc.html')

4.判断当前用户是否登录

request.user.is_authenticated()
def get_user(request):
    print(request.user)  # AnonymousUser  匿名用户
    print(type(request.user))  # <class'django.utils.functional'>
    print(request.user.is_authenticated())  # 判断当前用户是否登录,返回布尔值
    return HttpResponse('ok')

5.校验原密码是否正确

request.user.check_password(old_password)

6.修改密码

request.user.set_password(new_password)
request.user.save() # 修改密码一定要保存
# @login_required(login_url='/lgg/')  # 局部配置,如没有登录,让用户直接跳到登录页面
@login_required
def check_password(request):
    if request.method == "POST":
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        # 校验老密码是否正确
        is_right = request.user.check_password(old_password)
        if is_right:
            request.user.set_password(new_password)
            request.user.save()  # 修改密码一定要保存
    user_obj = request.user
    return render(request,'change_password.html',locals())

7.注销

auth.logout(request)
@login_required
def logout(request):
    # request.session.flush()
    auth.logout(request)
    return HttpResponse('注销了')

8.校验用户是否登录装饰器

局部配置

from django.contrib.auth.decorators import login_required
@login_required(login_url='/login/')  # 局部配置
def index(request):
    pass

全局配置

settings配置文件中 直接配置
LOGIN_URL = '/login/'
@login_required
def index(request):
    pass
# 如果全局配置了 局部也配置  以局部的为准

auth扩展表

如何扩展auth_user表字段?

方式1
利用一对一外键字段关系

class UserDetail(models.Model):
    phone = models.BigIntegerField()
    user = models.OneToOneField(to='User')

方式2:

? 利用继承关系

首先先去配置文件settings中配置
AUTH_USER_MODEL = 'app01.Userinfo'  # 应用名.表名
之后所有的auth模块功能全都以你写的表为准,由于继承了AbstractUser,会将auth_user那张表与自己建的这张表整合到一起,auth_user本身所拥有的功能,userinfo也能照常使用,使表的操作性更强了。            
# 表字段写好之后,记得数据库迁移
from django.contrib.auth.models import AbstractUser
                class Userinfo(AbstractUser):
                phone = models.Bi  gIntegerField()
                register_time = models.DateField(auto_now_add=True)

基于django settings配置文件实现插拔式设计

conf文件夹、lib文件夹、start.py处于同级

# conf文件夹
# settings.py
NAME = '暴露给用户自定义配置'
#lib>conf文件夹

# __init__.py文件
import importlib
from lib.conf import global_settings
import os
class Settings(object):
    def __init__(self):
        # print(111111)
        for name in dir(global_settings):
            if name.isupper():
                setattr(self,name,getattr(global_settings,name))
        # 获取暴露给用户的配置文件字符串路径
        module_path = os.environ.get('xxx')
        md = importlib.import_module(module_path)  # md = settings
        for name in dir(md):
            if name.isupper():
                k = name
                v = getattr(md,name)
                setattr(self,k,v)
settings = Settings()

# global_settings.py文件
NAME = '项目默认的配置文件'
# start.py文件
import os
import sys

BASE_DIR = os.path.dirname(__file__)
sys.path.append(BASE_DIR)
if __name__ == '__main__':
    os.environ.setdefault('xxx','conf.settings')
    from lib.conf import settings
    print(settings.NAME)  # settings就是类Settings的对象(单例)

原文地址:https://www.cnblogs.com/zhangchaocoming/p/11993039.html

时间: 2024-08-03 23:19:33

django 之csrf、auth模块及settings源码、插拔式设计的相关文章

Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax csrf相关装饰器 在CBV上加csrf装饰器 Django settings源码剖析及模仿使用 Django settings源码剖析 查看内部配置文件 模仿使用 Auth模块 auth简介 auth模块常用方法 创建用户 校验用户名和密码 保存用户登录状态 判断当前用户是否登录 校验原密码 修改密

1205 CSRF跨站请求与django中的auth模块使用

目录 今日内容 昨日回顾 基于配置文件的编程思想 importlib模块 简单代码实现 跨站请求伪造csrf 1. 钓鱼网站 如何实现 模拟该现象的产生 2. 解决问题 解决 {% csrf_token %} 3. ajax如何解决 方式1 方式2 方式3 4. csrf相关的两个装饰器 1. 使用 2. 两个装饰器在CBV上的异同 django里settings源码剖析 django有两个配置文件 django auth模块 1. 是什么 2. 常用方法 2.1 创建用户 create_use

Django的View(视图)、settings源码的解析、模板层

一.FBV与CBV 视图函数并不只是指函数,也可以是类 FBV:基于函数的视图,类似面向函数式编程 CBV:基于类的视图,类似面向对象编程 研究解析render源码: render:返回html页面:并且能够给该页面传值 分析:FBV视图原理 from django.shortcuts import render,HttpResponse # Create your views here. from django.template import Template,Context # FBV解析

Django的settings源码分析

首先需要在settings文件中导入,然后查看settings源码 from django.conf import global_settings,settings 点进去LazySettings我们会发现它是一个类,所以settings是类实例化出来的对象,这里利用了单例模式. 然后我们点入manage.py文件中看到 最后点入Settings类 原文地址:https://www.cnblogs.com/baohanblog/p/12164257.html

Django settings源码解析

Django settings源码 Django中有两个配置文件 局部配置:配置文件settings.py,即项目同名文件夹下的settings.py文件 全局配置:django内部全局的配置文件settings.py,需要导入才能看到 from django.conf import settings # 是一个对象,单例模式 from django.conf import global_settings # 真正的默认配置文件 特点: 先加载全局配置,再加载局部配置,以局部优先 源码解析 点进

Android FM模块学习之源码分析(六)

现在是2015年1月啦,得改口说去年了,去年抽时间整理了一些FM模块的主要源码类的东西,今年再整理一下几个次要的类的源码.这样来看FM上层的东西不是太多. 请看android\vendor\qcom\opensource\fm\fmapp2\src\com\caf\fmradio\Settings.java protected void onCreate(BundlesavedInstanceState) 从FMRadio.java用使用Intent跳转携带参数过来,在onCreate获取携带数

基于Servlet、JSP、JDBC、MySQL的一个简单的用户注册模块(附完整源码)

最近看老罗视频,做了一个简单的用户注册系统.用户通过网页(JSP)输入用户名.真名和密码,Servlet接收后通过JDBC将信息保存到MySQL中.虽然是个简单的不能再简单的东西,但麻雀虽小,五脏俱全,在此做一归纳和整理.下面先上源码: 一.index.jsp <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path =

Android FM模块学习之四源码解析(二)

上一章我们了解了FM主activity:FMRadio.java,若没查看的,请打开链接Android FM模块学习之四源码解析(一) 查看fmradio.java源码注释.接下来我们来看看FM重要的一个类:FMRadioService.java 由上一章我们已经知道,打开FM时,在OnStart函数中会bindToService来开启服务, public boolean bindToService(Context context, ServiceConnection callback) { L

python附录-builtins.py模块str类源码(含str官方文档链接)

python附录-builtins.py模块str类源码 str官方文档链接:https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str builtins.py class str(object): """ str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str Create a new stri