csrf跨站請求、相關裝飾器、auth模組使用

  • 2022 年 3 月 11 日
  • 筆記

昨日內容回顧

  • django操作cookie和session
# 作用:就是保存用戶資訊,保存一系列數據,還可以做快取 保留一段時間

# session是基於cookie工作的
	1. 數據是保存在服務端
	2. 存儲的介質:
		01 文件
		02 資料庫
		03 redis
		3. 如果以後項目中使用了負載均衡,注意考慮是否有session共享的問題

# 如何使用session:
	1. 設置session
		request.session['username'] = 'aaa'
		request.session['id'] = 111
		'''
		1. 生成一個隨機字元串,
		2. 保存數據到資料庫中
			session_key  session_data
		3. 把數據加密處理之後保存的。
		'''
	2. 獲取session
		request.session['username']
		# session設置的時候,是被加密的,獲取出來的session值是明文的
		'''
		1. 先拿到sessionid  => session中的隨機字元串 session_key
		2. 會拿著sessionid的值去表中查詢
			如果有數據,進行反解密,在把數據封裝到request.session中,因此,我們直接可以通過request.session['id'] 取值
		'''
        
	# 以上操作是在中間件中處理的,sessionmiddleware
    
# 如何設置session的過期時間
	request.session.set_expity(120)
    
# 設置會話Session和Cookie的超時時間
	'''
		request.session.set_expiry(value)
		* 如果value是個整數,session會在些秒數後失效
		* 如果value是個datatime或timedelta,session就會在這個時間後失效
		* 如果value是0,用戶關閉瀏覽器session就會失效
		* 如果value是None,session會依賴全局session失效策略
	'''
  • CBV添加裝飾器
# FBV:直接在函數的頭上面添加裝飾器的名稱

# CBV:添加裝飾器與FBV添加裝飾器是有區別的
	cbv添加裝飾器需要藉助於一個裝飾器
    
from django.utils.decorators import method_dectorator

'''drf:django rest framework => '''
from django.views import View

'''
請求方式:8種, get, post,
	options
	put
	patch
在前後端分離項目中,會出現跨域問題 簡單請求,複雜請求
'''
@method_decorators(login_auth, 'get')
@method_decorators(login_auth, 'post')
@method_decorators(login_auth, 'patch')
class IndexView(View):
    # 重寫父類的方法
    @method_decorators()  # 給IndexView所有的方法添加裝飾器
    def dispatch(self, request, *args, **kwargs):
        # 依據:mro列表
        return super().dispatch(request, *args, **kwargs):
    
    @method_decorators(login_auth)
    def get(self, request):
        return HttpResponse('ok')
  • 中間件
# 1. 中間件的位置在哪裡?在路由層之前
	django提供了7個中間件,在配置文件中,我們是可以查詢這七個中間件的源碼的

# 2. csrf的中間件, 針對於post請求的提交做csrf的校驗

# 3. 支援我們自定義中間件
	3.1 在任意一個應用下,創建一個py文件
	3.2 寫一個類,類裡面有2個方法,類必須繼承middlewaremixin
	3.3 去配置文件中註冊中間件
    
# 需要我們掌握的是2個方法:
	1. process_request
		# 請求來的時候,會按照中間的註冊順序,依次從上往下執行
        
	2. procesS_resposne
		# 請求走的時候,會按照中間件的註冊順序,依次從下往上執行。
		'''
		視圖函數在所有中間件的process_request之後,所有中間件的process_response之前
		'''
        
		# 如果我們在某一個中間件中的process_reqeust返回了值,之後的中間件都不在走了,然後再走同級別的process_response方法
        
	# 以下3個都不重要(了解)
	3. process_view
	4. process_template
	5. process_excption
    
# 使用場景:
	1. 對全棧做許可權校驗
	2. 頻率限制
	3. 認證

今日內容概要

  • csrf跨站請求
  • csrf的處理(提供的有相關裝飾器)
  • Auth模組
    • B端產品(business,eg:crm,)
    • C端產品(consumer面向用戶的,eg:淘寶,京東,租號玩)

內容詳細

1. csrf跨站請求

# 背景:
	釣魚網站
'''
英語4級報名網站為例
	你要在這個網站要付費,你去的這個網站是一個冒牌的網站,就去冒牌網站裡面付費了,錢付到了冒牌網站,但是沒有報名成功
'''
	會出現在form表單中,action參數:朝後端發送的地址
    
# 怎麼解決這個問題?
	csrf是針對與post請求的才會做驗證
"""
token相關的報錯:
	一般都是一個串:秘鑰 私鑰 公鑰...
""" 

# 兩種解決方式
# views.py中添加功能:
def index(request):
    return render(request, 'index.html')

# urls.py中添加路由:
	url(r'^index/', views.index),

# 新建index.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="//cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="//cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
{#<form action="" method="post">#}
{#    {% csrf_token %}  {# 解決csrf_token相關報錯 方法一 #}
{#    <p>username<input type="text" name="username"></p>#}
{#    <p>password<input type="text" name="password"></p>#}
{#    <input type="submit">#}
{#</form>#}

<button class="btn">ajax</button>
<script>    {# 解決csrf_token相關報錯 方法二 #}
$('.btn').click(function () {
    $.ajax({
        url: '',
        type: 'post',
        {#data:{'a':1, 'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()},  {# 有form表單這樣寫 #}
        data: {'a': 1, 'csrfmiddlewaretoken': '{{ csrf_token }}'}, {# 沒有form表單這樣寫 #}
        success: function () {

        }
    })
})
</script>
</body>
</html>

image

image

2. csrf相關裝飾器

# 如果使用中間件限制的話,就會限制全局的,要麼全部限制,要麼全部不限制

# 兩種情況
	只有index函數需要驗證,其他的不需要驗證
	只想讓home函數不驗證,其他的都需要驗證

# 提供了2個裝飾器
	csrf_protect: 需要驗證
	csrf_exempt: 不需要驗證
	'''按照FBV和CBV的使用即可'''
    
    
# 在views.py中測試:

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
from django.views import View

'''
    針對CBV:
        csrf_protect的三種方式都是可以的
        csrf_exempt前兩種方式都不行,只有第三種方式可以的
'''
# @method_decorator(csrf_protect, 'post')  # 2.針對CBV需要驗證 方式二
# @method_decorator(csrf_exempt, 'post')  # 5.這種方式也不行
# class IndexView(View):
    # @method_decorator(csrf_protect)  # 1.針對CBV需要驗證 方式一
    # @method_decorator(csrf_exempt)  # 4.這種方式不可以
    # def post(self, request):
    #     return HttpResponse('post')

class IndexView(View):
    # @method_decorator(csrf_protect)  # 3.針對CBV需要驗證 方式三
    @method_decorator(csrf_exempt)  # 6.這種方式是可以的
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)
    def post(self, request):
        return HttpResponse('post')

3. Auth模組

# 遷移資料庫會有一個表生成,auth_user
# django項目創建完後之後,會有一個默認的路由,admin/
# admin/是django默認提供的後台管理介面

# 訪問admin/
	登錄參照的數據就是從auth_user表中來,前提是必須是超級管理員

# 如何創建超級管理員?
	python3 manage.py createsuperuser

image

image

image

# auth模組實現登錄功能

# 在views.py中測試:
def home(request):
    return render(request, 'home.html')
from django.contrib import auth

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 1.去哪個表查詢登錄數據:auth_user
        # 2.密碼如何比對
        """
        注意事項:
            用戶名與密碼必須兩者同時傳入
        """
        obj = auth.authenticate(request, username=username, password=password)
        # print(obj)  # None 密碼不正確返回的值 結果是當前對象
        # print(obj)  # root 密碼正確返回的值 結果是當前對象
        # print(obj.username)
        # print(obj.password)

        if obj:
            # 保存用戶資訊 auth模組有全套功能需要使用
            auth.login(request, obj)  # 相當於保存了用戶資訊
            """
            一旦執行了上面的這句話 就可以在全局任何地方 通過request.user 拿到用戶對象
            """
            return redirect('/home/')
    return render(request, 'login.html')



# login.html內容:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="//cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="//cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<form action="" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>password:<input type="text" name="password"></p>
    <input type="submit">
</form>
</body>
</html>


# urls.py添加路由:
	url(r'^home/', views.home),
	url(r'^login/', views.login),

# home.html寫入:
	body標籤內:<h1>home頁</h1>

image

image

# auth模組驗證是否登錄(裝飾器)

from django.contrib.auth.decorators import login_required

"""
登錄裝飾器跳轉的地址有兩種:
    1.局部的 
        @login_required(login_url=login)
        
    2.全局的 在settings.py文件中配置
        LOGIN_URL = '/login/' 
        
    如果同時存在局部的和全局的 那麼優先訪問 局部 的地址
"""

@login_required  # 判斷是否登錄裝飾器 代替了下面的 if判斷
def func(request):
    # 必須登錄之後才能訪問
    print(request.user)  # AnonymousUser 未登錄狀態下訪問
    print(request.user)  # root 用戶名 登錄狀態下訪問
    print(request.user.is_authenticated())  # 驗證是否登錄 False True
    # if request.user.is_authenticated():
    #     return HttpResponse('func')
    # else:
    #     return redirect('/login/')
    
    
# 添加路由:
	url(r'^func/', views.func),

image

# auth模組實現修改密碼

@login_required
def set_pwd(request):
    if request.method == 'POST':
        old_pwd = request.POST.get('old_pwd')
        new_pwd = request.POST.get('new_pwd')
        again_pwd = request.POST.get('again_pwd')

        # 1. 先判斷兩次密碼輸入是否一致
        if new_pwd == again_pwd:
            # 2. 校驗老密碼是否正確
            is_right = request.user.check_password(old_pwd)
            print(is_right)  # 返回的是布爾值
            if is_right:
                # 3. 修改密碼
                request.user.set_password(new_pwd)  # 這句話 並沒有把密碼修改到資料庫里 只是修改了password密碼的屬性
                request.user.save()  # 這句話才是真正的操作數據了
        return redirect('/home/')

    return render(request, 'set_pwd.html')


# 添加路由:
	url(r'^set_pwd/', views.set_pwd),

image

# auth模組實現註銷功能

def logout(request):
    # 清空cookie和session
    # request.session.delete()
    # request.session.flush()

    auth.logout(request)
    return redirect('/login/')


# 添加路由:
	url(r'^logout/', views.logout),
# auth模組實現註冊用戶功能

from django.contrib.auth.models import User  # 相當於使用 auth_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)  # 密碼是加密的
        
        return redirect('/home/')
    
    
# 添加路由:
	url(r'^register/', views.register),
Tags: