程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

django進階

編輯:Python

Python之django進階

文章目錄

    • Python之django進階
      • 一、批量插入數據
      • 二、分頁類的使用
      • 三、cookie介紹
        • 1、什麼是cookie
        • 2、cookie的原理
        • 3、cookie版驗證登錄
        • 4、關於cookie的更多操作
      • 四、session介紹
        • 1、session相關方法
        • 2、session相關解析流程圖
        • 3、session版驗證登錄
      • 五、CBV版添加裝飾器
      • 六、中間件
        • 1、什麼是中間件
        • 2、自定義中間件
        • 3、process_request
        • 4、process_response
        • 5、process_view
        • 6、process_exception
        • 7、process_template_response
        • 8、中間件執行流程圖
        • 9、中間件版的登錄驗證
      • 七、csrf跨站請求偽造
        • 1、什麼是csrf,csrf攻擊原理
        • 2、csrf攻擊防范
        • 3、csrf在Django中的應用
          • 1、在form表單中使用
          • 2、在Ajax中的使用
        • 4、csrf裝飾器
          • 1、csrf全局禁用
          • 2、csrf局部禁用
      • 八、auth模塊
        • 1、auth模塊是什麼
        • 2、auth模塊常用方法
          • 1、auth.authenticate()
          • 2、auth.login()
          • 3、request.user.is_authenticated()
          • 4、logout()
          • 5、login_requierd()
          • 5、create_user()
          • 6、create_superuser()
          • 7、check_password(password)
          • 8、set_password(password)
          • 10、User對象的屬性
        • 3、auth模塊表的擴展

一、批量插入數據

  • bulk_create()
def pl_insert(request):
for i in range(10000):
models.user.objects.create(title='第%s行'%i)
data_all = models.user.objects.all()
return render(request,'all.html',locals())
""" 上述代碼執行時將近耗時8秒多(很可怕,這才一萬條數據) """
def pl_insert(request):
pl_list = []
for i in range(100000):
pl_obj = models.user(title='第%s行'%i)
pl_list.append(pl_obj)
data_all = models.user.bulk_create(pl_list)
return render(request,'all.html',locals())
""" 第二種代碼只運行了三秒(效果非常好) """
# 總結:在批量插入數據時可以使用orm提供的fulk_create()來減少操作時間

二、分頁類的使用

  • 在左側欄新建lib文件夾將以下代碼放置到一個py文件中
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
""" 封裝分頁相關數據 :param current_page: 當前頁 :param all_count: 數據庫中的數據總條數 :param per_page_num: 每頁顯示的數據條數 :param pager_count: 最多顯示的頁碼個數 """
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
# 總頁碼
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果總頁碼 < 11個:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 總頁碼 > 11
else:
# 當前頁如果<=頁面上最多顯示11/2個頁碼
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 當前頁大於5
else:
# 頁碼翻到最後
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 添加前面的nav和ul標簽
page_html_list.append(''' <nav aria-label='Page navigation>' <ul class='pagination'> ''')
first_page = '<li><a href="?page=%s">首頁</a></li>' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一頁</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一頁</a></li>' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一頁</a></li>'
else:
next_page = '<li><a href="?page=%s">下一頁</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '<li><a href="?page=%s">尾頁</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加標簽
page_html_list.append(''' </nav> </ul> ''')
return ''.join(page_html_list)
  • 後端操作
from lib.mypage import Pagination
def booklist(request):
# 算出所有數據的總條數
all_count = models.book.objects.count()
# 拿到當前是那一頁
current_page = request.GET.get('page',1)
# 調用分頁庫
obj = Pagination(current_page,all_count)
# 傳給前端html格式
page_html = obj.page_html()
# 取出數據的開始和結束
all_data = models.book.objects.all()[obj.start:obj.end]
return render(request,'booklist.html',locals())
  • 前端操作
{% for foo in all_data %}
<p >
{
{ foo.title }}
</p>
{% endfor %}
{
{ page_html|safe }}

三、cookie介紹

1、什麼是cookie

Cookie具體指的是一段小信息,它是服務器發送出來存儲在浏覽器上的一組組鍵值對,下次訪問服務器時浏覽器會自動攜帶這些鍵值對,以便服務器提取有用信息。

2、cookie的原理

cookie的工作原理是:由服務器產生內容,浏覽器收到請求後保存在本地;當浏覽器再次訪問時,浏覽器會自動帶上Cookie,這樣服務器就能通過Cookie的內容來判斷這個是“誰”了。

3、cookie版驗證登錄

  • 前端代碼
login_cookie.html
<div  class="center">
<form action="" METHOD="post" >
<p>
username:<input type="text" name="username" >
</p>
<p>
password:<input type="password" name="password">
</p>
<input type="submit" class="btn btn-success" id="button">
</form>
</div>
home_cookie.html
<h1>你好,我是登錄後的頁面~(cookie)</h1>
<a href="/del_session/" class="btn btn-danger">退出登錄</a>
  • 後端代碼
from django.shortcuts import render,HttpResponse,redirect
# Create your views here.
""" cookie版驗證登錄 """
# 登陸裝飾器
def login_auth_cookie(func):
def inner(request,*args,**kwargs):
path = request.get_full_path()
""" 檢測用戶當前路徑,用於用戶點擊其他頁面也需要用戶登錄 """
if request.COOKIES.get('key'): # 這裡使用request.COOKIES.get判斷用戶到底是否登錄
return func(request,*args,**kwargs)
else:
# 用戶沒有登錄,其先前點擊的路徑將會拼接到登錄頁面的後面
return redirect('/login/?next=%s'%path)
return inner
def login_cookie(request):
if request.method =='POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'egon' and password == '123':
print(username,password)
next = request.GET.get('next') # 獲取用戶/login/後面的路徑
if next:
# 用戶一旦登錄跳轉至用戶之前打開位置的網址
res = redirect(next)
res.set_cookie(key='key', value='123')
else:
# 用戶沒有點擊其他網頁跳轉會首頁網頁
res = redirect('/home_cookie/')
res.set_cookie(key='key', value='123')
return res
# 用戶密碼有誤或者不登錄將一直在登陸頁面
return render(request, 'login_cookie.html')
# 登錄成功後所實現的頁面的方法
@login_auth_cookie
def home_cookie(request):
return render(request, 'home_cookie.html')
# 驗證裝飾器及其非登錄進入頁面檢測路徑的方法
@login_auth_cookie
def index_cookie(request):
return HttpResponse('這裡是index頁面(cookie)')
# 用戶退出後,清除cookie的方法
def del_cookie(request):
res = redirect('/login_cookie/')
res.delete_cookie('key')
return res

4、關於cookie的更多操作

獲取cookie

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
# 參數
default:默認值
salt:加密鹽
max_age:後台控制時間

設置cookie

rep = HttpResponse(...)
rep = render(request, ...)
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)
""" key:鍵 value:值 max_age=None 超時時間 expires=None, 超時時間(IE requires expires, so set it if hasn't been already.) path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問 domain=None, Cookie生效的域名 secure=False, https傳輸 httponly=False 只能http協議傳輸,無法被JavaScript獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋) """

刪除cookie

def logout(request):
rep = redirect("/login/")
rep.delete_cookie("user") # 刪除用戶浏覽器上之前設置的usercookie值
return rep

四、session介紹

1、session相關方法

# 獲取、設置、刪除Session中數據
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在則不設置
del request.session['k1']
# 所有 鍵、值、鍵值對
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()
# 會話session的key
request.session.session_key
# 將所有Session失效日期小於當前日期的數據刪除
request.session.clear_expired()
# 檢查會話session的key在數據庫中是否存在
request.session.exists("session_key")
# 刪除當前會話的所有Session數據
request.session.delete()
# 刪除當前的會話數據並刪除會話的Cookie。
request.session.flush()
這用於確保前面的會話數據不可以再次被用戶的浏覽器訪問
例如,django.contrib.auth.logout() 函數中就會調用它。
# 設置會話Session和Cookie的超時時間
request.session.set_expiry(value)
* 如果value是個整數,session會在些秒數後失效。
* 如果value是個datatime或timedelta,session就會在這個時間後失效。
* 如果value是0,用戶關閉浏覽器session就會失效。
* 如果value是None,session會依賴全局session失效策略。

2、session相關解析流程圖

3、session版驗證登錄

  • 前端代碼
login_session.html
<div >
<form action="" method="post">
<p>
username:<input type="text" name="username">
</p>
<p>
password:<input type="text" name="password">
</p>
<input type="submit" class="btn btn-success">
</form>
</div>
home.html
<h1>你好,我是登錄後的頁面~(session)</h1>
<a href="/del_session/" class="btn btn-danger">退出登錄</a>
  • 後端代碼
from django.shortcuts import render,HttpResponse,redirect
# Create your views here.
""" session版驗證登錄 """
# 登陸裝飾器
def login_auth_session(func):
def inner(request,*args,**kwargs):
path = request.get_full_path()
""" 檢測用戶當前路徑,用於用戶點擊其他頁面也需要用戶登錄 """
if request.session.get('name'): # 這裡使用request.session.get('xxx')判斷用戶到底是否登錄
return func(request,*args,**kwargs)
# 用戶沒有登錄,其先前點擊的路徑將會拼接到登錄頁面的後面
return redirect('/login_session/?next=%s'%path)
return inner
# 登錄頁面
from app01 import models
def login_session(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
login_obj = models.user.objects.filter(username=username).first()
if login_obj:
if login_obj.password == int(password):
request.session['name'] = username
next = request.GET.get('next') # 獲取用戶/login/後面的路徑
if next:
res = redirect(next) # 如果路徑存在,登錄成功後跳轉到該頁面
return res
res = redirect('/home_session/') # 如果路徑不存在跳轉至首頁
return res
# 用戶密碼有誤或者不登錄將一直在登陸頁面
return render(request,'login_session.html')
# 登錄成功後所實現的頁面的方法
@login_auth_session
def home_session(request):
return render(request,'home_session.html')
# 驗證裝飾器及其非登錄進入頁面檢測路徑的方法
@login_auth_session
def index_session(request ):
return HttpResponse('我是通過裝飾器登錄的頁面')
# 用戶點擊退出登錄,清除session的方法
def del_session(request):
request.session.flush() # 清空客戶端和服務端(推薦)
request.session.delete() # 清空服務端
return redirect('/login_session/')

五、CBV版添加裝飾器

在上面的添加裝飾器均是在類中添加,那麼在views.py視圖函數內如何給類添加裝飾器?

CBV版登錄認證

  • 後端代碼
from django.views import View
class login_cbv(View):
def get(self,request):
return render(request,'login_cbv.html')
def post(self,request):
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'egon' and password == '123':
return HttpResponse('POST請求登錄')
return render(request,'login_cbv.html')
  • 前端代碼
<div >
<form action="" METHOD="post" >
<p>
username:<input type="text" name="username" >
</p>
<p>
password:<input type="password" name="password">
</p>
<input type="submit" class="btn btn-success" id="button">
</form>
</div>

CBV添加裝飾器版的登錄認證(三種方式)

# 需要先導入模塊
from django.utils.decorators import method_decorator
  • 後端代碼
from django.views import View
# 裝飾器
def login_decor(func):
def inner(request,*args,**kwargs):
path = request.get_full_path()
""" 檢測用戶當前路徑,用於用戶點擊其他頁面也需要用戶登錄 """
if request.session.get('name'): # 這裡使用request.session.get('xxx')判斷用戶到底是否登錄
return func(request, *args, **kwargs)
# 用戶沒有登錄,其先前點擊的路徑將會拼接到登錄頁面的後面
return redirect('/login_session/?next=%s' % path)
return inner
from django.utils.decorators import method_decorator
""" 直接加在視圖類上,但method_decorator必須傳 name 關鍵字參數 get方法和post方法都需要登錄校驗的話就寫兩個裝飾器 """
# 登錄模塊
@method_decorator(login_decor,name='get')
@method_decorator(login_decor,name='post')
class login_cbv(View):
""" 加在dispatch中表示包含在 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] 內所有的請求都添加裝飾器 因為CBV中首先執行的就是dispatch方法,所以這麼寫相當於給get和post方法都加上了登錄校驗。 """
@method_decorator(login_decor)
def dispatch(self, request, *args, **kwargs):
return super(login_cbv, self).dispatch(request, *args, **kwargs)
""" 單獨加在CBV視圖的get或post方法上表示對單個請求加裝飾器 """
@method_decorator(login_decor)
def get(self,request):
return render(request,'login_cbv.html')
def post(self,request):
username = request.POST.get('username')
password = request.POST.get('password')
login_obj = models.user.objects.filter(username=username).first()
if login_obj:
if login_obj.password == int(password):
request.session['name'] = username
next = request.GET.get('next') # 獲取用戶/login/後面的路徑
if next:
res = redirect(next) # 如果路徑存在,登錄成功後跳轉到該頁面
return res
res = HttpResponse('POST請求登錄') # 如果路徑不存在跳轉至首頁
return res
return render(request,'login_cbv.html')
  • 前端代碼
<div >
<form action="" METHOD="post" >
<p>
username:<input type="text" name="username" >
</p>
<p>
password:<input type="password" name="password">
</p>
<input type="submit" class="btn btn-success" id="button">
</form>
</div>

六、中間件

1、什麼是中間件

在學Django生命周期圖的時候,中間件在web網關服務接口與路由層中間,所以我們可以知道當請求來的時候是先走中間件的,後走路由層的。那麼我們就能想到,是不是對中間件的操作就能影響路由層與視圖層呢?

官方關於中間件的文檔:中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局范圍內改變Django的輸入和輸出。每個中間件組件都負責做一些特定的功能。但是由於其影響的是全局,所以需要謹慎使用,使用不當會影響性能

說的直白一點中間件是幫助我們在視圖函數執行之前和執行之後都可以做一些額外的操作,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。

這些中間件通常存在與settings文件下的MIDDLEWARE列表內,我們的每一次請求都離不開他們

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

2、自定義中間件

中間可以自定義的方法有五個(主要掌握process_request和process_response)

  • process_request(self, request)
  • process_response(self, request, response)
  • process_view(self, request, view_func, view_args, view_kwargs)
  • process_template_response(self, request, response)
  • process_exception(self, request, exception)

上面的方法都可返回None或者Httpresponse對象,如果返回None那麼Django將繼續向下執行視圖層的代碼等等。但是如果返回了Httpresponse對象,那麼Django將不會繼續執行下面路由層及視圖層的代碼會將Httpresponse對象優先返回給用戶

自定義中間件示例

""" 首先在項目下建立一個middleware文件夾(名字可以隨便起) 在middleware文件夾內建立一個py文件 文件內書寫以下代碼 """
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,HttpResponse,redirect
class Mymiddleware1(MiddlewareMixin):
def process_request(self,request):
print('這是第一個自定義中間中的process_request')
def process_response(self,request,response):
print('這是第一個自定義中間中的process_response')
return HttpResponse('Mymiddleware1')
class Mymiddleware2(MiddlewareMixin):
def process_request(self,request):
print('這是第二個自定義中間中的process_request')
def process_response(self,request,response):
print('這是第二個自定義中間中的process_response')
return HttpResponse('Mymiddleware1')
""" 將此文件導入settings文件內並在MIDDLEWARE內添加以下代碼 """
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.MyMiddleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'MyMiddleware.ware.Mymiddleware1', # 自定義中間件1
'MyMiddleware.ware.Mymiddleware2', # 自定義中間件2
]
# 此時終端打印結果
""" 這是第一個自定義中間中的process_request 這是第二個自定義中間中的process_request 這是第二個自定義中間中的process_response 這是第一個自定義中間中的process_response """
由此可以總結出:
當Http請求來的時候中間件的執行順序是自上而下執行
當後端給返回浏覽器響應數據時中間件是自上而下執行

3、process_request

process_request(self, request)有個參數request,這個request和視圖函數中request是一樣的(在交給Django後端的路由之前,對這個request對象可以進行一系列操作)

由於request對象是一樣的,所以我們可以對request對象進行一系列的操作,包括request.變量名=變量值,這樣的操作,我們可以在後續的視圖函數中通過相同的方式即可獲取到我們在中間件中設置的值。

總結:

  • 中間件的process_request方法是在執行視圖函數之前執行的。
  • 當配置多個中間件時,會按照MIDDLEWARE中的注冊順序,也就是列表的索引值,從前到後依次執行的。
  • 不同中間件之間傳遞的request都是同一個對象

4、process_response

process_response(self, request, response) ,多個中間件中的process_response方法是按照MIDDLEWARE中的注冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一個中間件的process_request方法最後一個執行,它的process_response方法是最先執行。

定義process_response方法時,必須給方法傳入兩個形參,request和response。request就是上述例子中一樣的對象,response是視圖函數返回的HttpResponse對象(也就是說這是Django後台處理完之後給出一個的一個具體的視圖)。該方法的返回值(必須要有返回值)也必須是HttpResponse對象。如果不返回response而返回其他對象,則浏覽器不會拿到Django後台給他的視圖,而是我的中間件中返回的對象

5、process_view

process_view(self, request, view_func, view_args, view_kwargs)

該方法有四個參數

request是HttpRequest對象。

view_func是Django即將使用的視圖函數。 (它是實際的函數對象,而不是函數的名稱作為字符串。)

view_args是將傳遞給視圖的位置參數的列表.

view_kwargs是將傳遞給視圖的關鍵字參數的字典。 view_args和view_kwargs都不包含第一個視圖參數(request)。

Django會在調用視圖函數之前調用process_view方法。

它應該返回None或一個HttpResponse對象。 如果返回None,Django將繼續處理這個請求,執行任何其他中間件的process_view方法,然後在執行相應的視圖。 如果它返回一個HttpResponse對象,那麼將不會執行Django的視圖函數,而是直接在中間件中掉頭,倒敘執行一個個process_response方法,最後返回給浏覽器

process_view方法是在Django路由系統之後,視圖系統之前執行的,執行順序按照MIDDLEWARE中的注冊順序從前到後順序執行

6、process_exception

process_exception(self, request, exception)

該方法兩個參數:

一個HttpRequest對象

一個exception是視圖函數異常產生的Exception對象。

這個方法只有在視圖函數中出現異常了才執行,它返回的值可以是一個None也可以是一個HttpResponse對象。如果是HttpResponse對象,Django將調用模板和中間件中的process_response方法,並返回給浏覽器,否則將默認處理異常。如果返回一個None,則交給下一個中間件的process_exception方法來處理異常。它的執行順序也是按照中間件注冊順序的倒序執行

7、process_template_response

process_template_response(self, request, response)

它的參數,一個HttpRequest對象,response是TemplateResponse對象(由視圖函數或者中間件產生)。

process_template_response是在視圖函數執行完成後立即執行,但是它有一個前提條件,那就是視圖函數返回的對象有一個render()方法(或者表明該對象是一個TemplateResponse對象或等價方法)

8、中間件執行流程圖

9、中間件版的登錄驗證

中間件版的登錄驗證需要依靠session,所以數據庫中要有django_session表。

  • view.py
# 中間件版的登錄認證
def login_middle(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
login_obj_middle = models.user.objects.filter(username=username).first()
if login_obj_middle:
if login_obj_middle.password == int(password):
next = request.GET.get('next')
request.session['name'] = username
if next:
return redirect(next)
return redirect('/func_middle/')
return render(request,'login_middle.html')
def func_middle(request):
return HttpResponse('你好我是首頁~')
def index_middle(request):
return HttpResponse('這是index_middle頁面,要經過中間件認證才能到達')
  • MyMiddleware.ware.py
class auth_middleware(MiddlewareMixin):
def process_request(self,request):
path = request.path_info
# 最最重要,如果不判斷其路徑是否是登錄路徑會造成重定向次數過多的情況
if not path.startswith('/login_middle/'):
if not request.session.get('name'):
return redirect('/login_middle/?next={}'.format(path))
  • settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.MyMiddleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# 'MyMiddleware.ware.Mymiddleware1', # 自定義中間件1
# 'MyMiddleware.ware.Mymiddleware2', # 自定義中間件2
'MyMiddleware.ware.auth_middleware'
]

七、csrf跨站請求偽造

1、什麼是csrf,csrf攻擊原理

csrf:跨站請求偽造

csrf攻擊原理:攻擊者盜用了你的身份,以你的名義發送惡意請求,然而對服務器來說這個請求完全是合法的

要完成一次csrf攻擊,受害者必須依次完成以下兩個步驟

  • 登錄受信任的網站A,並在本地生成Cookie
  • 在不退出A的情況下,訪問B(不安全)

2、csrf攻擊防范

""" 防止釣魚網站的思路 網站會給用戶訪問的form表單頁面 偷偷塞一個隨機字符串 該隨機字符串有以下特點 1.同一個浏覽器每一次訪問都不一樣 2.不同浏覽器絕對不會重復 請求到來的時候 會先比對隨機字符串是否一致 如果不一致 直接拒絕(403) """

3、csrf在Django中的應用

1、在form表單中使用
<form action="" method="post">
{
% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>password:<input type="text" name="password"></p>
<input type="submit">
</form>
  • 這時再次打開網頁檢查源碼,你會看到如下代碼
<input type="hidden" name="csrfmiddlewaretoken" value="vhTcZWUvUFPq6QaMwMMn7792WX1wJD7zPMjFHybaFbmygFeQUipvtijXeyItthj6">
  • 下一次提交給後端時後端就會校驗name="csrfmiddlewaretoken"的值(這就是隨機字符串,永遠都不一樣),如果一樣就允許操作,不一樣直接拒絕(403 Forbidden)

  • 在前端加上{% crsf_token %},後端settings裡的MIDDLEWARE中的關於csrf的中間件以後就不用注釋掉了

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.MyMiddleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# 'MyMiddleware.ware.Mymiddleware1', # 自定義中間件1
# 'MyMiddleware.ware.Mymiddleware2', # 自定義中間件2
'MyMiddleware.ware.auth_middleware'
]
2、在Ajax中的使用
  • 有兩種方法,通過修改ajax中data

方法一、先在form表單頁面上寫{% csrf_token%},利用標簽查找,獲取到該input鍵值消息

data{
'username':'xxxx','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}

方法二、直接書寫’{ {csrf_token}}’

data{
'username':'xxx','csrfmiddlewaretoken':'{
{csrf_token}}'}
  • 還可以將獲取隨機鍵值對的方法,寫到一個js文件中,然後導入這個文件即可

新建一個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);
}
}
});

4、csrf裝飾器

1、csrf全局禁用

直接注釋掉settings中MIDDLEWARE的中間件 ‘django.middleware.csrf.CsrfViewMiddleware’,

2、csrf局部禁用
  • 在FBV中使用,需要導入模塊,直接加在FBV上(在頁面不加{% csrf_token %}的情況下)
from django.views.decorators.csrf import csrf_exempt,csrf_protect
#(前提是全局使用,沒有注釋csrf) 讓這個不用校驗,可以局部使用
#當你網站全局需要校驗csrf的時候,有幾個不需要校驗該如何處理
@csrf_exempt(全局校驗就免除)
def login(request):
return HttpResponse('login')
#(前提是全局禁用,注釋csrf,不會進行校驗) 設置就會進行校驗,局部禁用
#當你網站全局不校驗csrf的時候,有幾個需要校驗又該如何處理
@csrf_protect(全局不校驗就保護)
def lll(request):
return HttpResponse('lll')
  • 在CBV中使用,只能在dispatch方法或者類上面
from django.views import View
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator
# 這兩個裝飾器在給CBV裝飾的時候 有一定的區別
如果是csrf_protect 那麼有三種方式
# 第一種方式
# @method_decorator(csrf_protect,name='post') # 有效的
class MyView(View):
# 第三種方式
# @method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self,request):
return HttpResponse('get')
# 第二種方式
# @method_decorator(csrf_protect) # 有效的
def post(self,request):
return HttpResponse('post')
如果是csrf_exempt 只有兩種(只能給dispatch裝) 特例
@method_decorator(csrf_exempt,name='dispatch') # 第二種可以不校驗的方式
class MyView(View):
# @method_decorator(csrf_exempt) # 第一種可以不校驗的方式
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self,request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post')

八、auth模塊

1、auth模塊是什麼

Auth模塊是Django自帶的用戶認證模塊

django在啟動之後可以直接訪問admin路由,但是需要登錄,數據的參考就是auth_user表,並且還必須得是管理員用戶才能進入

創建超級用戶(管理員)

  • python3 manage.py createsuperuser

2、auth模塊常用方法

  • 使用auth模塊要用就用全套
from django.contrib import auth
1、auth.authenticate()
auth.authenticate() # 去auth_user表中自動校驗數據
user_obj = auth.authenticate(request,username=username,password=password)
user_obj.username
user_obj.password
……
""" 返回的是一個對象,auth.authenticate()類裡有__str__方法 1、自動查找auth_user標簽 2、自動給用戶輸入的密碼加密後做比對 用戶名和密碼是必須要傳的 數據不符合則返回None """
2、auth.login()
# 用戶登錄後保存用戶的登錄狀態
auth_login(request,user_obj) # 類似於request.session[key] = user_obj
""" 只要執行了該方法你就可以在整個項目的任意地方通過request.user拿到當前登陸的用戶對象 其原理就是: 自動取django_session表中查找對應的用戶對象再封裝到request.user中 如果此對象不存在那麼request.user返回的是AnonymousUser(匿名用戶) """
3、request.user.is_authenticated()
# 判斷用戶是否登錄
request.user.is_authenticated()
""" 返回True/False """
4、logout()
# 該函數接受一個HttpRequest對象,無返回值。
def logout_view(request):
auth.logout(request)
# Redirect to a success page.
""" 當調用該函數時,當前請求的session信息會全部清除。該用戶即使沒有登錄,使用該函數也不會報錯。 """
5、login_requierd()
# 一個裝飾器工具,用來快捷的給某個視圖添加登錄校驗。
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
""" 若用戶沒有登錄,則會跳轉到django默認的 登錄URL '/accounts/login/ ' 並傳遞當前訪問url的絕對路徑 (登陸成功後,會重定向到該路徑)。 如果需要自定義登錄的URL,則需要在settings.py文件中通過LOGIN_URL進行修改。 """
@login_required(login_url='/login/') # 局部配置【使用不當會造成重定向次數過多】
LOGIN_URL = '/login/' # 這裡配置成你項目登錄頁面的路由(全局配置)
優先級 局部大於全局
局部和全局使用視情況而定
path = request.get_full_path()
if not path.endswith('/app02/login/'):
next = request.GET.get('next')
return redirect(next)
return HttpResponse('haha')
@login_required
def home(request):
return HttpResponse('OK')
@login_required
def index(request):
return HttpResponse('INDEX')
5、create_user()
# auth 提供的一個創建新普通用戶的方法,需要提供必要參數(username、password)等。
from django.contrib.auth.models import User
user = User.objects.create_user(username='用戶名',password='密碼',email='郵箱',...)
6、create_superuser()
# auth 提供的一個創建新的超級用戶的方法,需要提供必要參數(username、password)等。
from django.contrib.auth.models import User
user = User.objects.create_superuser(username='用戶名',password='密碼',email='郵箱',...)
7、check_password(password)
# auth 提供的一個檢查密碼是否正確的方法,需要提供當前請求用戶的密碼。
ok = user.check_password("老密碼",'新密碼')
""" 密碼正確返回True,否則返回False。 """
8、set_password(password)
# auth 提供的一個修改密碼的方法,接收 要設置的新密碼 作為參數。
""" 注意:設置完一定要調用用戶對象的save方法!!! """
user.set_password(password='')
user.save()
10、User對象的屬性
""" User對象屬性:username, password is_staff : 用戶是否擁有網站的管理權限. is_active : 是否允許用戶登錄, 設置為 False,可以在不刪除用戶的前提下禁止用戶登錄。 """

3、auth模塊表的擴展

我們可以通過繼承內置的 AbstractUser 類,來定義一個自己的Model類。

這樣既能根據項目需求靈活的設計用戶表,又能使用Django強大的認證系統了。

from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
""" 用戶信息表 """
nid = models.AutoField(primary_key=True)
phone = models.CharField(max_length=11, null=True, unique=True)
def __str__(self):
return self.username
""" 如果繼承了AbstractUser,在執行數據庫遷移命令時 auth_user表不會再被創建出來,被創建出的表名則是我們自己定義的類名 而Userinfo表中會出現auth_user表中所有的字段且還有Userinfo自己擴展的字段 前提: 1、在繼承之前數據庫遷移命令沒有被執行(auth_user表沒有被創建出來) 2、繼承的類AbstractUser裡面的字段名不要被覆蓋 """

注意:

按上面的方式擴展了內置的auth_user表之後,一定要在settings.py中告訴Django,我現在使用我新定義的UserInfo表來做用戶認證。寫法如下:

# 引用Django自帶的User表,繼承使用時需要設置
AUTH_USER_MODEL = "app名.UserInfo"

一旦我們指定了新的認證系統所使用的表,我們就需要重新在數據庫中創建該表,而不能繼續使用原來默認的auth_user表了。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved