[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方法
利用字符串的形式導入模塊前端
import importlib res = 'lib.bbb' md = importlib.import_moudle(res) print(md)
首先定義一個配置路徑列表 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)
你本身寫一個跟中國銀行正規網站如出一轍的頁面 用戶輸入用戶名 密碼 對方帳戶 轉帳金額提交 請求確實是朝中國銀行的接口發送的 錢也扣了 可是對方帳戶變了 變成了釣魚網站本身提早設置好的帳戶
你在寫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>
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>
{% csrf_token %}
data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},
先在頁面任意的位置上書寫{% csrf_token %}
算法
直接在data中書寫'csrfmiddlewaretoken':'{{ csrf_token }}
鍵值對數據庫
data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},
官網提供的文件 最通用的一種方式django
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); } } });
將下面的文件配置到你的Django項目的靜態文件中,在html頁面上經過導入該文件便可自動幫咱們解決ajax提交post數據時校驗csrf_token的問題,(導入該配置文件以前,須要先導入jQuery,由於這個配置文件內的內容是基於jQuery來實現的)編程
更多細節詳見:Djagno官方文檔中關於CSRF的內容後端
兩個裝飾器,分別制定視圖函數方法哪一個須要校驗csrf,或不須要校驗的瀏覽器
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')
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這個裝飾器只能給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方式全均可以 跟你普通的裝飾器裝飾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')
另外的配置文件settings在 from django.conf import settings 中
一個是暴露給用戶能夠配置的 一個是內部全局的(用戶配置了就用用戶的 用戶沒有配就用本身的) obj obj.name = 'egon' # 全局 obj.name = 'jason' # 局部
思考題 參看django settings源碼 應用到本身的項目中 在你的項目中 實現配置文件的插拔式設計 用戶配置了就用用戶的 用戶沒有配就用本身的
Auth模塊是Django自帶的用戶認證模塊:
咱們在開發一個網站的時候,無可避免的須要設計實現網站的用戶系統。此時咱們須要實現包括用戶註冊、用戶登陸、用戶認證、註銷、修改密碼等功能,這還真是個麻煩的事情呢。
Django做爲一個完美主義者的終極框架,固然也會想到用戶的這些痛點。它內置了強大的用戶認證系統--auth,它默認使用 auth_user 表來存儲用戶數據。
建立超級用戶(root)
python3 manage.py createsuperuser tool中直接createsuperuser
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') # 建立超級用戶 須要郵箱數據
auth.authenticate
導入auth模塊
from django.contrib import auth
校驗用戶名和密碼是否正確 user_obj = auth.authenticate(request,username=username,password=password) # 必須傳用戶名和密碼兩個參數缺一不能
login(request,對象)
auth.login(request,user_obj) # 只要這句話執行了 後面在任意位置 只要你能拿到request你就能夠經過request.user獲取到當前登陸的用戶對象
is_authenticated()
request.user.is_authenticated()
check_password(舊密碼)
request.user.check_password(old_password)
set_password(新密碼)
獲取 request.user.set_password(new_password) request.user.save() # 千萬不要忘了
logout(request)
auth.logout(request) # request.session.flush()
@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
若是全局配置了 局部也配置 以局部的爲準
利用外鍵一對一進行擴展
class UserDetail(models.Model): phone = models.BigIntegerField() user = models.OneToOneField(to='User')
利用面向對象的繼承
導入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()