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

[toc]html

今日内容

今日内容:
    
	基于配置文件的编程思想
	
	跨站请求伪造csrf
	
	django auth模块
	
	django settings源码剖析
	
	基于django settings源码实现本身项目配置文件插拔式设计

昨日回顾

cookie与session
		cookie与session发展史
			因为http协议是无状态的 因此咱们须要发明一些可以保存用户的技术
		
		cookie
			保存在客户端浏览器上面的键值对(能够有多个)
			cookie是后端服务器控制与设值的 浏览器只是奉命行事
			浏览器也有权利拒绝写入cookie(一切须要登陆认证的网站全都没法正常登陆)
			
			如何查看浏览器上面的cookie
				application/COOKIES/信息
		
		session
			保存在服务端上的键值对(能够有多个)
			
		django如何操做cookie
			前提是须要用到HttpResponse对象
			obj = HttpResponse()
			obj1 = render()
			obj2 = redirect()
			
			设值cookie
				obj.set_cookie('k1','v1')
			获取cookie
				request.COOKIES.get('k1')
			设值失效时间
				obj.set_cookie('k1','v1',max_age=10)
				obj.set_cookie('k1','v1',expires=5)
				都以秒为单位expires针对IE浏览器
			删除cookie
				obj.delete_cookie('k1')
			
			基于cookie实现用户登陆校验
				装饰器
				用户在没有登陆以前若是访问了一个须要登陆以后才能访问的页面
				那么会先跳转到登陆页面 用户输入用户名和密码并正确的状况下
				再自动跳转到以前用户想要访问的页面
					1.request.path_info
					2.利用get请求携带参数的方法
	
	
		session操做
			django默认的session失效时间是14天,你也能够设置
			session是保存在服务端的 默认状况下 你的django须要先执行数据库迁移命令
			由于保存sesson数据的表须要先提早建立出来,若是不建立会报错(no such table:django_session)
			session不仅仅能够放在django_session表中  也能够放在任意可以做为数据库的地方
				文件
				内存
				缓存
				...
				配置文件
				
			django的session表中的数据是针对浏览器的 同一个浏览器只会存一行数据
				
			
			设置session
				request.session['k1'] = 'v1'
				"""
					1.django内部自动调用算法 生成一个随机字符串
					2.将随机字符串与设置的值保存到django_session表中(真正写入数据的操做是在django中间件里面的session相关中间件发生的)
					3.将随机字符串返回给浏览器保存到cookie
						sessionid:随机字符串
				"""
			获取session
				request.session.get('k1')
				"""
					1.django内部会自动获取浏览器发送过来的cookie中的获取sessionid所对应的随机字符串
					2.拿着随机字符串去django_session表中比对
					3.若是有拿出数据放到request.session中供用户调用
				"""
			删除session
				request.session.delete()
				request.session.flush()
			失效时间
				request.session.set_expiry()
					1.数字(不能是0)   秒数
					2.数字0    浏览器关闭自动失效
					3.时间格式数据  datetime
					4.不写    默认参考的是全局的失效时间
			
			基于session实现用户登陆校验
	
	django中间件
		django中间件就相似于django的门户或者保安
		全部的请求和响应都必须通过中间件才可以正常经过
		而且在中间件中能够对请求和响应的数据进行处理
		django中间件在设计到一些全局方面的功能时 做用很是大
			1.网站全局的用户登陆校验
			2.网站全局的访问频率校验
			3.网站全局的用户权限校验
			...
			只要是全局的功能,你都应该第一个想到中间件
		
		django默认有七个中间件 每一个中间件都有本身独立的功能 若是你想用 你直接注释掉便可
		django还支持用户自定义本身的中间件 而且暴露给用户五个能够自定义的方法
		
		前提
			1.只要是设计处处理前端业务逻辑的视图函数 都须要有形参request
			2.若是你想自定义中间件 你必须写一个类而且继承MiddlewareMixin
			3.一旦形参中reponse你就必须返回  由于response就是后端想要返回前端的数据
		五个方法
			须要掌握的
				1.process_request(self,request)  (******)
					1.请求到来的时候 会按照配置文件注册的从上往下的顺序依次通过每个中间件里面的该方法
						若是中间件中没有定义该方法 直接跳过执行下一个
					2.一旦该方法返回了一个HttpResponse对象 那么请求再也不日后走 而是走到同级别的process_response依次返回
				
				2.process_response(self,request,response)
					1.响应走的时候 会按照配置文件注册的从下往上的顺序依次通过每个中间件里面的该方法
						该方法默认必须返回response 若是你返回了本身的HttpResponse对象 那么用户收到的就是你的
			须要了解的
				3.process_view(self,request,view_name,*args,**kwargs)
					1.路由匹配成功以后执行视图函数以前
				4.process_exception(self,request,response)
					1.视图函数中出现错误的时候自动触发
				5.process_template_response(self,request,response)
					1.返回的对象中含有render方法

基于配置文件的编程思想

importlib模块

利用字符串的形式导入模块前端

import importlib
res = 'lib.bbb'
md = importlib.import_moudle(res)
print(md)

img

简单代码实现

首先定义一个配置路径列表
NOTIFY_LIST = [
    'notify.email.Email',
    'notify.msg.Msg',
    'notify.wechat.WeChat'
]

-------------------------------------------------------


在包的__init__文件中设置send_all函数

# import settings
import importlib
def send_all(countent):
    # 将settings文件中的存储函数路径迭代循环
    for path in settings.NOTIFY_LIST:
        # 经过.切割,获取每个文件的的路径,以及cls类名
        module_path,cls_name = path.rsplit('.',maxsplit=1)
        # 经过importlib方法传入路径,获取导入文件对象
        md = importlib.import_module(module_path)
        # getattr反射获取md文件中的相对应cls类
        cls = getattr(md,cls_name)
        # 实例化类,获取一个个类的对象
        obj = cls()
        # 调用类中的发送方法
        obj.send(countent)

跨站请求伪造csrf

1. 钓鱼网站

你本身写一个跟中国银行正规网站如出一辙的页面
用户输入用户名 密码 对方帐户  转帐金额提交
请求确实是朝中国银行的接口发送的 钱也扣了
可是对方帐户变了 变成了钓鱼网站本身提早设置好的帐户

如何实现

你在写form表单的时候 让用户填写的对方帐户input并无name属性
而是你本身在内部偷偷隐藏了一个具备name属性的input框
而且value值是你本身的帐户 而后将该标签隐藏了

模拟该现象的产生

建立两个django项目
------------------------------------------------------
html
<p>假的</p>
//  提交的地址是真正的网站地址
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>username<input type="text" name="username"></p>
    <p>
        target_account:
        <input type="text">
        <input type="text" name="target_account" value="jason" style="display: none;">
        // 经过设置两个input框,并隐藏自定义value的框,得到该白转帐人的目的
    </p>

    <p>money:<input type="text" name="money"></p>
    <input type="submit">
    
------------------------------------------------------
    
<p>这是正经的网站</p>
<form action="" method="post">
    <p>username<input type="text" name="username"></p>
    <p>target_account<input type="text" name="target_account"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>

2. 解决问题

django中的中间件`'django.middleware.csrf.CsrfViewMiddleware',`就是负责校验csrf的

只处理本网站发送的post请求python

如何识别如何判断当前请求是不是本网站发出的
**防护CSRF攻击:**

    目前防护 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。

解决 {% csrf_token %}

在from表单中添加 {% csrf_token %}ajax

<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>

img

3. ajax如何解决

方式1

  • 先在页面任意的位置上书写{% csrf_token %}
  • 而后在发送ajax请求的时候 经过标签查找获取随机字符串添加到data自定义对象便可
data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},

方式2

  • 先在页面任意的位置上书写{% csrf_token %}算法

  • 直接在data中书写'csrfmiddlewaretoken':'{{ csrf_token }}键值对数据库

  • data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},

方式3

官网提供的文件 最通用的一种方式django

  • 直接新建js文件拷贝代码,进行导入 script
  • 不须要作任何csrf相关的代码书写
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);
    }
  }
});

将下面的文件配置到你的Django项目的静态文件中,在html页面上经过导入该文件便可自动帮咱们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件以前,须要先导入jQuery,由于这个配置文件内的内容是基于jQuery来实现的)编程

更多细节详见:Djagno官方文档中关于CSRF的内容后端

4. csrf相关的两个装饰器

两个装饰器,分别制定视图函数方法哪一个须要校验csrf,或不须要校验的浏览器

1. 使用

导入

from django.views.decorators.csrf import csrf_exempt,csrf_protect

不校验 @csrf_exempt

@csrf_exempt           # 不校验 csrf
def index(request):   
	return HttpResponse('index')

校验 @csrf_protect

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

2. 两个装饰器在CBV上的异同

cbv

CBV就是在url中一个路径对应一个类
	基于FBV的模式就是在Django的路由映射表里进行url和视图函数的关联,而基于CBV的模式则是在views.py文件中定义视图类,在视图类中视图函数,如get,post,put,delete等
	
	
在写代码中的几点注意事项:
    cbv定义类的时候必需要继承view
    在写url的时候必需要加as_view
    类里面使用form表单提交的话只有get和post方法
    类里面使用ajax发送数据的话支持定义如下不少方法
    restful规范:
    'get'获取数据, 'post'建立新数据, 'put'更新, 'patch'局部更新, 'delete'删除, 'head', 'options', 'trace'

----------------------------------------------------
视图函数中

from django.views import View
 
class IndexView(View):
   # 以get形式访问会执行get函数,通常状况下获取数据
   def get(self, *args, **kwargs):
      return HttpResponse('666')
 
   # 以post形式访问的话会执行post函数,通常状况下发送数据
   def post(self, *args, **kwargs):
      return HttpResponse('999')

@ csrf_exempt

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

# @method_decorator(csrf_exempt,name='post')  # csrf_exempt不支持该方法
				@method_decorator(csrf_exempt,name='dispatch')  # csrf_exempt
				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')

@ csrf_protect

csrf_protect方式全均可以 跟你普通的装饰器装饰CBV一致

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

另外的配置文件settings在
from django.conf import settings  中

django有两个配置文件

一个是暴露给用户能够配置的
			
			一个是内部全局的(用户配置了就用用户的 用户没有配就用本身的)
			obj
			obj.name = 'egon'  # 全局
			obj.name = 'jason'  # 局部
  • 先加载全局配置 给对象设置
  • 而后在加载局部配置 再给对象设置
  • 一旦有重复的项 后者覆盖前者
思考题
    参看django settings源码 应用到本身的项目中 
    在你的项目中 实现配置文件的插拔式设计
    用户配置了就用用户的 用户没有配就用本身的

img

img

img

django auth模块

1. 是什么

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

咱们在开发一个网站的时候,无可避免的须要设计实现网站的用户系统。此时咱们须要实现包括用户注册、用户登陆、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。

Django做为一个完美主义者的终极框架,固然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。

建立超级用户(root)

python3 manage.py createsuperuser

tool中直接createsuperuser

img

2. 经常使用方法

2.1 建立用户 create_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')  # 建立超级用户   须要邮箱数据

2.2 校验 auth.authenticate

导入auth模块

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

2.3 保存登陆 login(request,对象)

auth.login(request,user_obj)
# 只要这句话执行了 后面在任意位置 只要你能拿到request你就能够经过request.user获取到当前登陆的用户对象

2.4 判断是否登陆 is_authenticated()

request.user.is_authenticated()

2.5 校验原密码是否正确 check_password(旧密码)

request.user.check_password(old_password)

2.6 修改密码 set_password(新密码)

获取
request.user.set_password(new_password)
request.user.save()  # 千万不要忘了

2.7 注销 logout(request)

auth.logout(request)

# request.session.flush()

2.8 登陆装饰器 @login_required

导入

from django.contrib.auth.decorators import login_required

局部配置

能够自定义未登陆的报错跳转界面login_url = '/跳转链接/'

@login_required(login_url='/login/')
def index(request):
	pass

全局配置

settings配置文件中 直接配置
LOGIN_URL = '/login/'

而后在视图函数添加
@login_required
def index(request):
	pass

若是全局配置了 局部也配置 以局部的为准

3. 扩展auth_user表字段

方式1

利用外键一对一进行扩展

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

方式2

利用面向对象的继承

导入AbstractUser类
	from django.contrib.auth.models import AbstractUser
	
定义模型表类
    class Userinfo(AbstractUser):
        phone = models.BigIntegerField()
        register_time = models.DateField(auto_now_add=True)
        
settings配置文件中设置
	AUTH_USER_MODEL = 'app01.Userinfo'  # 应用名.表名

执行数据库迁移命令

# 这么写完以后 以前全部的auth模块功能全都以你写的表为准

扩展

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

# 执行start文件中
if __name__ == '__main__':
	设置全局大字典的键值 键(随便) 值(文件的路径)
	os.environ['xxx'] = 'conf.settings'
    form lib.conf import settings
    print(settings.NAME)
    
有两个settings配置文件,分别对应暴露给用户,以及隐藏的全局配置
各自有NAME属性


------------ conf.settings.__init__配置-------------------
class Settings(object):
    def __init__(self):
        # 获取全局配置中的全部变量名
        for name in dir(global_settings):
            # 筛选出须要的大写变量名
            if name.isupper():
                # 给self对象设置全局配置中的大写变量名 属性值
                setattr(self,name,getattr(globals_settings,name))

        # 获取暴露给用户的配置文件字符串路径
        module_path = os.environ.get('xxx')
        md = importlib.import_module(module_path)
        for name in dir(md):
            if name.isupper():
                k = name
                v = getattr(md,name)
                setattr(self,k,v)

# 实例化类得到对象
settings = Settings()

相关文章
相关标签/搜索