目錄
一、Cookie與Session
1.1 概念
1.2 Django實作的cookie
1.2.1 擷取Cookie
1.2.2 設定Cookie
1.2.3 删除Cookie
1.3 Django實作的Session
1.3.1 基本操作
1.3.2 Session使用示例
1.3.3 将登入驗證寫成裝飾器
1.3.4 Session存儲的相關配置
二、Django的使用者認證
2.1 auth子產品
2.1.1 authenticate()
2.1.2 login(HttpRequest, user)
2.1.3 logout(request) 登出使用者
2.1.4 user對象的 is_authenticated()
2.2 User對象
2.2.1 is_authenticated()
2.2.2 建立使用者
2.2.3 check_password(passwd)
2.2.4 修改密碼
2.2.5 示例
一、Cookie與Session
1.1 概念
cookie不屬于http協定範圍,由于http協定無法保持狀态,但實際情況,我們卻又需要“保持狀态”,是以cookie就是在這樣一個場景下誕生。
cookie工作原理:
使用者在浏覽器登入後,服務端根據使用者的登入資訊生成鍵值對(也就是cookie),儲存使用者的個人資訊,然後将鍵值對傳回給浏覽器,浏覽器儲存在本地。當浏覽器再次通路時,就會自動将這個鍵值對(cookie)帶上,這樣服務端就能通過cookie的内容判斷這個使用者是誰了。
cookie雖然在一定程度上解決了保持狀态的需求,但由于cookie本身最大支援4096位元組的資料,以及cookie本身是明文儲存在用戶端,很容易被攔截或竊取。是以就需要一種能夠支援儲存更多資料,并且儲存在服務端,且具有較高安全性的機制,這就是session。
session工作原理:
session是基于cookie工作的。
- 使用者登入後,服務端随機生成一個字元串,将這個随機字元串作為鍵值對(cookie)的value,而鍵值對的key則是由Django的session配置中自定義,預設情況下是sessionid,組成的cookie就是{'sessionid':'xxxxxxxxxx'},服務端将這個cookie傳回給浏覽器,這樣,浏覽器本地隻儲存着一個随機字元串;
- 服務端再将第1步中生成的随機字元串做為key,由使用者資訊産生的字典做為value,組成一個鍵值對,儲存在伺服器端;假設随機字元串是"abc123",那組成的鍵值對理論上類似這樣:{'123abc':{'login':true,'username':'alex'}}
- 下次使用者通路時,浏覽器帶着cookie,服務端根據cookie中的随機字元串就可以找到對應的使用者資訊了。
session的好處:浏覽器端隻存在一個随機字元串,避免使用者資訊被直接暴露在外。
注意:cookie和session是所有語言共通的,不限于語言和架構。
1.2 Django實作的cookie
1.2.1 擷取Cookie
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
參數:
default: 預設值
salt: 加密鹽
max_age: 背景控制過期時間
1.2.2 設定Cookie
request.set_cookie(key,value,...)
request.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回傳給帶有該路徑的頁面,這樣可以避免将cookie傳給站點中的其他的應用。/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面通路
domain=None, Cookie生效的域名,你可用這個參數來構造一個跨站cookie。如, domain=".example.com"所構造的cookie對下面這些站點都是可讀的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果該參數設定為 None ,cookie隻能由設定它的站點讀取。
secure=False, 如果設定為 True ,浏覽器将通過HTTPS來回傳cookie。
httponly=False 隻能http協定傳輸,無法JavaScript擷取(不是絕對,底層抓包可以擷取到也可以被覆寫)
由于cookie儲存在用戶端的電腦上,是以,JavaScript和jquery也可以操作cookie。
<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });
1.2.3 删除Cookie
request.delete_cookie("cookie_key",path="/",domain=name)
cookie存儲到用戶端
優點: 資料存在在用戶端,減輕伺服器端的壓力,提高網站的性能。
缺點: 安全性不高:在用戶端機很容易被檢視或破解使用者會話資訊
1.3 Django實作的Session
1.3.1 基本操作
1、設定Sessions值
request.session['session_name'] ="admin"
2、擷取Sessions值
session_name = request.session["session_name"]
3、删除Sessions值
del request.session["session_name"]
4、檢測是否操作session值
if "session_name" is request.session :
5、request.session.flush()
# 删除目前的會話資料并删除會話的Cookie。這用于確定前面的會話資料不可以再次被使用者的浏覽器通路,例如,django.contrib.auth.logout() 函數中就會調用它。
6、使用者session的随機字元串
request.session.session_key
7、将所有Session失效日期小于目前日期的資料删
request.session.clear_expired()
8、檢查 使用者session的随機字元串 在資料庫中是否
request.session.exists("session_key")
9、删除目前使用者的所有Session資料
request.session.delete("session_key")
10、設定session失效期限
request.session.set_expiry(value)
* 如果value是個整數,session會在些秒數後失效。
* 如果value是個datatime或timedelta,session就會在這個時間後失效。
* 如果value是0,使用者關閉浏覽器session就會失效。
* 如果value是None,session會依賴全局session失效政策。
1.3.2 Session使用示例
views.py
def log_in(request):
if request.method=="POST":
username=request.POST['user']
password=request.POST['pwd']
user=UserInfo.objects.filter(username=username,password=password)
if user:
#設定session内部的字典内容
request.session['is_login']='true'
request.session['username']=username
#登入成功就将url重定向到背景的url
return redirect('/backend/')
#登入不成功或第一通路就停留在登入頁面
return render(request,'login.html')
def backend(request):
print(request.session,"------cookie")
print(request.COOKIES,'-------session')
"""
這裡必須用讀取字典的get()方法把is_login的value預設設定為False,
當使用者通路backend這個url先嘗試擷取這個浏覽器對應的session中的
is_login的值。如果對方登入成功的話,在login裡就已經把is_login
的值修改為了True,反之這個值就是False的
"""
is_login=request.session.get('is_login',False)
#如果為真,就說明使用者是正常登陸的
if is_login:
#擷取字典的内容并傳入頁面檔案
cookie_content=request.COOKIES
session_content=request.session
username=request.session['username']
return render(request,'backend.html',locals())
else:
"""
如果通路的時候沒有攜帶正确的session,
就直接被重定向url回login頁面
"""
return redirect('/login/')
def log_out(request):
"""
直接通過request.session['is_login']回去傳回的時候,
如果is_login對應的value值不存在會導緻程式異常。是以
需要做異常處理
"""
try:
#删除is_login對應的value值
del request.session['is_login']
# OR---->request.session.flush() # 删除django-session表中的對應一行記錄
except KeyError:
pass
#點選登出之後,直接重定向回登入頁面
return redirect('/login/')
模版檔案:
===================================login.html==================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login/" method="post">
<p>使用者名: <input type="text" name="user"></p>
<p>密碼: <input type="password" name="pwd"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
===================================backend.html==================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>hello {{ username }}</h3>
<a href="/logout/">登出</a>
</body>
</html>
1.3.3 将登入驗證寫成裝飾器
def auth(func):
def warrper(req,*args,**kwargs):
try:
is_login=request.session.get('is_login',False)
#如果不為真,就說明使用者沒有登陸
if not is_login:
return redirect('/login/')
return func(req, *args, **kwargs)
except Exception as e:
return redirect('/login/')
return warrper
1.3.4 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,預設修改之後才儲存(預設)
注意:要想關閉浏覽器使session過期,SESSION_SAVE_EVERY_REQUEST和SESSION_EXPIRE_AT_BROWSER_CLOSE都必須為True
(2)存儲在緩存:
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,預設修改之後才儲存
(3)存儲在檔案:
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 緩存檔案路徑,如果為None,則使用tempfile子產品擷取一個臨時位址tempfile.gettempdir()
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,預設修改之後才儲存
(4)存儲在緩存+資料庫
資料庫用于做持久化,緩存用于提高效率
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
(5) 加密cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
二、Django的使用者認證
2.1 auth子產品
導入:
from django.contrib import auth
django.contrib.auth中提供了許多方法,這裡主要介紹其中的三個:
2.1.1 authenticate()
提供了使用者認證,即驗證使用者名以及密碼是否正确,一般需要username password兩個關鍵字參數;
如果認證資訊有效,會傳回一個 User 對象。authenticate()會在User 對象上設定一個屬性辨別那種認證後端認證了該使用者,且該資訊在後面的登入過程中是需要的。當我們試圖登陸一個從資料庫中直接取出來不經過authenticate()的User對象會報錯的!!
user = authenticate(username='someone',password='somepassword')
2.1.2 login(HttpRequest, user)
該函數接受一個HttpRequest對象,以及一個認證了的User對象。此函數使用django的session架構給某個已認證的使用者附加上session id等資訊。
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
2.1.3 logout(request) 登出使用者
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
該函數接受一個HttpRequest對象,無傳回值。當調用該函數時,目前請求的session資訊會全部清除。該使用者即使沒有登入,使用該函數也不會報錯。
2.1.4 user對象的 is_authenticated()
要求:
- 使用者登陸後才能通路某些頁面;
- 如果使用者沒有登入就通路該頁面的話直接跳到登入頁面;
- 使用者在跳轉的登陸界面中完成登陸後,自動通路跳轉到之前通路的位址。
方法一:
def my_view(request):
if not request.user.is_authenticated():
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
方法二:
django已經為我們設計好了一個用于此種情況的裝飾器:login_requierd()
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
若使用者沒有登入,則會跳轉到django預設的 登入URL '/accounts/login/ ' (這個值可以在settings檔案中通過LOGIN_URL進行修改)。并傳遞 目前通路url的絕對路徑 (登陸成功後,會重定向到該路徑)。
2.2 User對象
User 對象屬性:username, password(必填項)password用雜湊演算法儲存到資料庫
is_staff : 使用者是否擁有網站的管理權限.
is_active : 是否允許使用者登入, 設定為
False
,可以不用删除使用者來禁止 使用者登入
2.2.1 is_authenticated()
如果是真正的 User 對象,傳回值恒為 True 。 用于檢查使用者是否已經通過了認證。
通過認證并不意味着使用者擁有任何權限,甚至也不檢查該使用者是否處于激活狀态,這隻是表明使用者成功的通過了認證。 這個方法很重要, 在背景用
request.user.is_authenticated()
判斷使用者是否已經登入,如果true則可以向前台展示
request.user.name
2.2.2 建立使用者
使用 create_user 輔助函數建立使用者:
from django.contrib.auth.models import User
user = User.objects.create_user(username='',password='',email='')
2.2.3 check_password(passwd)
使用者需要修改密碼的時候 首先要讓他輸入原來的密碼 ,如果給定的字元串通過了密碼檢查,傳回 True。
2.2.4 修改密碼
使用 set_password() 來修改密碼
user = User.objects.get(username='')
user.set_password(password='')
user.save
2.2.5 示例
注冊:
def sign_up(request):
state = None
if request.method == 'POST':
password = request.POST.get('password', '')
repeat_password = request.POST.get('repeat_password', '')
email=request.POST.get('email', '')
username = request.POST.get('username', '')
if User.objects.filter(username=username):
state = 'user_exist'
else:
new_user = User.objects.create_user(username=username, password=password,email=email)
new_user.save()
return redirect('/book/')
content = {
'state': state,
'user': None,
}
return render(request, 'sign_up.html', content)
修改密碼:
@login_required
def set_password(request):
user = request.user
state = None
if request.method == 'POST':
old_password = request.POST.get('old_password', '')
new_password = request.POST.get('new_password', '')
repeat_password = request.POST.get('repeat_password', '')
if user.check_password(old_password):
if not new_password:
state = 'empty'
elif new_password != repeat_password:
state = 'repeat_error'
else:
user.set_password(new_password)
user.save()
return redirect("/log_in/")
else:
state = 'password_error'
content = {
'user': user,
'state': state,
}
return render(request, 'set_password.html', content)
三、事務
django支援事務,當你希望一段代碼是原子性操作時,就要用到事務。
什麼叫原子性操作?
- 原子性操作就是指要對一個事物進行一系列操作,而這一系列操作要麼全部成功,要麼 全部失敗。
- 舉個例子:假設你給小明轉賬。你賬戶有10元,小明賬戶也有10元。你需要給小明轉賬5元,那麼你的賬戶需要先減去5元,10-5=5。這時通過網絡通信,給小明賬戶加上5元,但是偏偏這時線路出了故障。這就造成你賬戶的錢已經被減去,而小明賬戶的錢卻沒有加上,錢就這樣不見了。我們當然不能允許這樣的事情發生了。事務就能将這兩個操作綁定在一起,如果第一步已經操作成功,而中途失敗了,那麼第一步操作也将恢複至原來的狀态。
Django中實作的事務非常簡單:
from django.db import transaction
with transaction.atomic():
'''
将需要支援事務的代碼寫在此處即可
'''