1. 程式人生 > 其它 >Django中的Session和cookie

Django中的Session和cookie

Session和cookie

  • 參考文獻:https://www.cnblogs.com/wupeiqi/articles/5246483.html

1.問題引入

1.1 cookie是什麼?

  • 儲存在客戶端瀏覽器上的鍵值對

1.2 Sessions

  • 儲存在服務端的資料(本質上是鍵值對)

    • 應用:依賴Cookie

    • 作用:保持會話(web網站)

    • 好處:敏感資訊不會直接給客戶端

1.3 簡單示例

2.cookie

  • cookie進行維持會話,一個瀏覽器只能儲存20個cookie,服務端上限為300cookie

    • 注意:cookie大小不能超過4kb
  • cookie一般不會設定中文

# 中文cookie的設定一般有兩種方式
# 1、使用decode('iso-8859-1').encode('utf-8')的方式
# 字串不能直接使用decode因此,注意使用順序
# 2、使用json,進行序列化,返回的方式為unique編碼的字串

2.1 簡單應用

  • 登入介面設定cookie防止其他非登入介面的人員進行訪問
from django.shortcuts import render,redirect,HttpResponse

# Create your views here.


def index(request):
    if request.COOKIES.get('is_login')=='True':
        return render(request,'index.html')
    else:
        return HttpResponse('你沒成功!')

def login(request):
    if request.method=='GET':
        return render(request,'login.html')
    if request.method=='POST':
        username=request.POST.get('username')
        pwd=request.POST.get('pwd')
        if username=='aaa' and pwd=='666':
            ret=redirect('index')
            # 設定cookie的鍵值對
            ret.set_cookie('is_login',True)
            print('訪問成功')
            return ret
        else:
            return redirect('login')
  • 注意:cookie雖然可以實現登入的驗證,但是會將狀態設定在客戶端瀏覽器,因此並不安全。

2.2 cookie的分發與驗證

  • 使用裝飾器進行封裝cookie的分發與驗證

  • 裝飾器的簡單複習
#編寫裝飾器
def wapper(f):
    #裝飾器的引數f為引用@下面的相關函式
    def inner(request,*args,**kwargs):
        # 決定執行先後的分界線
        ret=f(*args,**kwargs)
        
        return ret
    return inner
# 呼叫裝飾器
@wapper
def test():
    pass

'''
知識點
函式內部定義一個函式,該函式要呼叫一下原函式的引數
最外層函式的返回值為內層函式
裝飾器的呼叫
@name
*************
裝飾器與Python 的閉包相關聯,且知識點與js的函式引數可以為函式高度相似
'''
  • 裝飾器的cookie
from django.shortcuts import render,redirect,HttpResponse

# Create your views here.


#編寫裝飾器
def wapper(f):
    def inner(request,*args,**kwargs):
        is_loin=request.COOKIES.get('is_login')
        if is_loin=='True':
            ret=f(request,*args,**kwargs)
            return ret
        else:
            return redirect('login')
    return inner

# 呼叫裝飾器
@wapper
def index(request):
    return render(request,'index.html')

# 登入函式設定cookie不變
def login(request):
    if request.method=='GET':
        return render(request,'login.html')
    if request.method=='POST':
        username=request.POST.get('username')
        pwd=request.POST.get('pwd')
        if username=='aaa' and pwd=='666':
            ret=redirect('index')
            ret.set_cookie('is_login',True)
            # print('訪問成功')
            return ret
        else:
            return redirect('login')
<!--相關的兩個html檔案-->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陸介面</title>
</head>
<body>

    <h1>前進,前進,不擇手段的前進</h1>
    <hr>
    <form method="post" action="">
        {% csrf_token %}
        使用者名稱:<input type="text" name="username">
        <br>
        密碼:<input type="password" name="password">
        <button type="submit">提交</button>
    </form>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>自然選擇,前進四</h1>
</body>
</html>

2.3設定cookie的有效期

ret.set_cookie('name', 'AA',10)
ret.set_cookie('sex', 'male',10)

2.5 cookie的其他設定

rep = HttpResponse(...) 或 rep = render(request, ...)
 
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密鹽',...)
    引數:
        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獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)

2.6 cookie的缺點

  • cookie鍵值對存在與本地的瀏覽器檔案安全性較低

例如

def login(request):
    if request.method=='GET':
        return render(request,'login.html')
    if request.method=='POST':
        username=request.POST.get('username')
        pwd=request.POST.get('pwd')
        if username=='aaa' and pwd=='666':
            ret=redirect('index')
            ret.set_cookie('is_login',True)
            ret.set_cookie('name', 'AA')
            ret.set_cookie('sex', 'male')
            # print('訪問成功')
            return ret
        else:
            return redirect('login')

3.seesion會話

  • session的相關資料(鍵值對)預設儲存在服務端的資料庫中django_session表中。

  • 一個使用者,一個瀏覽器只儲存一條記錄

  • seesion的預設過期時間是兩週,可以手動設定

  • 生成cookie為加密後的字串,資料儲存在django_session資料表之中

    • 可以在setting-global裡面修改seesion_id的名稱,與資料庫表中的鍵相對應。

3.1 一般使用

def login(request):
    """ 使用者名稱和密碼登入 """
    if request.method == 'GET':
        form = LoginForm(request)
        return render(request, 'login.html', {'form': form})
    form = LoginForm(request,data=request.POST)
    if form.is_valid():
        # 使用者輸入正確,登陸成功
        username = form.cleaned_data['username']
        password = form.cleaned_data['password']
        # 賬號可能為郵箱或者是手機號
        '''
            查詢邏輯:username=手機號或者是郵箱,但是驗證碼均等於驗證碼
                    (username=moblie_phone and password=password)or(username=email and password=password)
            屬於複雜查詢條件,因此使用orm的Q查詢
        '''
        user_obj = models.UserInfo.objects.filter(Q(email=username) | Q(moble_phone=username)).filter(
            password=password).first()
        if user_obj:
            '''登入成功為1'''
            # session使用步驟
            # 1.生成隨機字串
            # 2.通過cookie傳送到客戶端
            # 3.儲存到服務端值
            request.session['username'] = user_obj.username
            # 上句程式碼可完成上述的所有操作。
            request.session.set_expiry(60 * 60 * 24 * 14)  # 設定session的時間為兩週
            return redirect('home')
        # 登入失敗
        form.add_error('username', '使用者名稱或密碼錯誤')
    return render(request, 'login.html', {'form': form})

3.2session和cookie的有效期

  • seesion的預設過期時間是兩週,可以手動設定
request.session.set_expiry(60 * 60 * 24 * 14)
  • 邏輯可參考上述(一般使用)程式碼
from django.shortcuts import render,redirect,HttpResponse

# Create your views here.

def index(request):

    # is_login=request.session['is_login']
    '''
        1.從cookie裡面拿出了session_id:xxx隨機字串拿出來
        2、去django-session表裡面查詢到相對應的資料
        3.反解密使用者儲存的資料,並獲取使用者的資料
    '''
    is_login = request.session.get('is_login')
    if is_login==True:
        return render(request,'index.html')
    else:
        return redirect('home')
def login(request):
    if request.method=='GET':
        return render(request,'login.html')
    if request.method=='POST':
        username=request.POST.get('username')
        pwd=request.POST.get('password')
        if username=='aa' and pwd=='300':
            # 設定seession
            request.session['is_login']=True
            request.session['name']='AA'
            '''
                1、生成ssesion_id:隨機字串
                2、在COOKIE裡面加上了鍵值對:session_id:ZDsrf
                3.將使用者的資料進行加密並儲存到DJANGO-SESSION表裡面,形式為一條記錄
                # 資料庫中session_key  隨機字串
                # seesion_data:使用者加密的資料
            
            '''
            return redirect('index')#redict裡面的字串為URL的別名,進行反向解析
        else:
            return HttpResponse('請正常登入')

3.3 seession的裝飾器

裝飾器函式一般要寫在被裝飾函式的上方,以免語法錯誤

def session_author(fn):

    def inner(request,*args,**kwargs):
        status=request.session.get('is_login')
        if status:
            ret=fn(request)
            return ret
        else:
            # return redirect('login'),#使用reverse進行反向解析
            return redirect(reverse('login'))
    return inner

與cookie的裝飾器相似。

  • 注:上述裝飾器可用作判斷使用者是否登入,但是在django中使用較少。django中一般使用中介軟體判斷使用者是否登入。並設定對應的白名單。

3.4 Session操作。

  • 可以進行增刪改查。

  • 增加就是登入時的寫入,修改可以直接像字典一樣進行修改。

  • 刪除seesion

    def logout(request):
        # 清楚Session
        request.session.flush()
        return redirect('home')
    

    常用作使用者登出登入的實現。

  • 獲取seesion

    中介軟體中獲取判斷

    class AuthMiddleware(MiddlewareMixin):
    
        def proccess_request(self,request):中
            '''如果使用者已登入,則request中賦值'''
            user_name=request.session.get('user_name',0)
            # 在中介軟體中獲取值,不存在,則預設值是0,獲取到使用者的user_name
            userobj=models.UserInfo.objects.filter(user_name=user_name)
            # 根據使用者的username去資料庫中進行查詢,查詢到結果userobj賦值給request
            # 中,如果request中的tracer有值,則認定為已經登入,反之則認為沒登入。
            request.tracer=userobj
            # 如果tracer的值比較多,則可以定義一個類,專門進行儲存
    
  • 完整登入中介軟體模板
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import datetime
    from django.shortcuts import redirect
    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    
    from web import models
    
    
    class Tracer(object):
    
        def __init__(self):
            self.user = None
            self.price_policy = None
            self.project = None
    
    
    class AuthMiddleware(MiddlewareMixin):
    
        def process_request(self, request):
            """ 如果使用者已登入,則request中賦值 """
    
            request.tracer = Tracer()
    
            user_id = request.session.get('user_id', 0)
            user_object = models.UserInfo.objects.filter(id=user_id).first()
            request.tracer.user = user_object
    
            # 白名單:沒有登入都可以訪問的URL
            """
            1. 獲取當用戶訪問的URL
            2. 檢查URL是否在白名單中,如果再則可以繼續向後訪問,如果不在則進行判斷是否已登入
            """
            if request.path_info in settings.WHITE_REGEX_URL_LIST:
                return
            # 檢查使用者是否已登入,已登入繼續往後走;未登入則返回登入頁面。
            if not request.tracer.user:
                return redirect('login')
    
            # 登入成功之後,訪問後臺管理時:獲取當前使用者所擁有的額度
    
            # 方式一:免費額度在交易記錄中儲存
    
            # 獲取當前使用者ID值最大(最近交易記錄)
            _object = models.Transaction.objects.filter(user=user_object, status=2).order_by('-id').first()
            # 判斷是否已過期
            current_datetime = datetime.datetime.now()
            if _object.end_datetime and _object.end_datetime < current_datetime:
                _object = models.Transaction.objects.filter(user=user_object, status=2, price_policy__category=1).first()
    
            request.tracer.price_policy = _object.price_policy
    
            # 方式二:免費的額度儲存配置檔案
            """
            # 獲取當前使用者ID值最大(最近交易記錄)
            _object = models.Transaction.objects.filter(user=user_object, status=2).order_by('-id').first()
    
            if not _object:
                # 沒有購買
                request.price_policy = models.PricePolicy.objects.filter(category=1, title="個人免費版").first()
            else:
                # 付費版
                current_datetime = datetime.datetime.now()
                if _object.end_datetime and _object.end_datetime < current_datetime:
                    request.price_policy = models.PricePolicy.objects.filter(category=1, title="個人免費版").first()
                else:
                    request.price_policy = _object.price_policy
            """
    
        def process_view(self, request, view, args, kwargs):
    
            # 判斷URL是否是以manage開頭,如果是則判斷專案ID是否是我建立 or 參與
            if not request.path_info.startswith('/manage/'):
                return
    
            project_id = kwargs.get('project_id')
            # 是否是我建立的
            project_object = models.Project.objects.filter(creator=request.tracer.user, id=project_id).first()
            if project_object:
                # 是我建立的專案的話,我就讓他通過
                request.tracer.project = project_object
                return
    
            # 是否是我參與的專案
            project_user_object = models.ProjectUser.objects.filter(user=request.tracer.user, project_id=project_id).first()
            if project_user_object:
                # 是我參與的專案
                request.tracer.project = project_user_object.project
                return
    
            return redirect('project_list')
    
    

3.5 Session配置檔案補充及儲存

1.資料庫儲存
Django預設支援Session,並且預設是將Session資料儲存在資料庫中,即:django_session 表中。
 
a. 配置 settings.py
 
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(預設)
     
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie儲存在瀏覽器上時的key,即:sessionid=隨機字串(預設)
SESSION_COOKIE_PATH = "/"                               # Session的cookie儲存的路徑(預設)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie儲存的域名(預設)
SESSION_COOKIE_SECURE = False                            # 是否Https傳輸cookie(預設)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支援http傳輸(預設)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(預設)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否關閉瀏覽器使得Session過期(預設)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次請求都儲存Session,預設修改之後才儲存(預設)
 
 
 
b. 使用
 
    def index(request):
        # 獲取、設定、刪除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的隨機字串
        request.session.session_key
 
        # 將所有Session失效日期小於當前日期的資料刪除
        request.session.clear_expired()
 
        # 檢查 使用者session的隨機字串 在資料庫中是否
        request.session.exists("session_key")
 
        # 刪除當前使用者的所有Session資料
        request.session.delete("session_key")
 		# "session_key"指的是 request.session.session_key
        request.session.set_expiry(value)
            * 如果value是個整數,session會在些秒數後失效。
            * 如果value是個datatime或timedelta,session就會在這個時間後失效。
            * 如果value是0,使用者關閉瀏覽器session就會失效。
            * 如果value是None,session會依賴全域性session失效策略。
2.快取Session
  • 快取,儲存在另一臺(快取伺服器)機器記憶體中的資料。一般結合redis使用
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的快取別名(預設記憶體快取,也可以是memcache),此處別名依賴快取的設定
 
 
    SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie儲存在瀏覽器上時的key,即:sessionid=隨機字串
    SESSION_COOKIE_PATH = "/"                                # Session的cookie儲存的路徑
    SESSION_COOKIE_DOMAIN = None                              # Session的cookie儲存的域名
    SESSION_COOKIE_SECURE = False                             # 是否Https傳輸cookie
    SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支援http傳輸
    SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否關閉瀏覽器使得Session過期
    SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次請求都儲存Session,預設修改之後才儲存
 
 
 
b. 使用
 	同上
3.其他

檔案Session

快取+資料庫Session

加密cookie Session

以上方式請點選參考文獻進行觀看。

4.裝飾器的補充

def warper(f):
    def inner(*args,**kwargs):
        print('我是執行前的語句')
        ret=f()
        print('我是執行後的語句')
        return ret# 返回原函式的返回結果
    return inner

@warper#執行wapper函式把test1當做引數
def tes1():
    print('***********')
    return '我是原函式的返回結果'
print(tes1())

5.知識梳理

繼續努力,終成大器!