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

相關文章
相關標籤/搜索