目錄
一、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():
'''
將需要支持事務的代碼寫在此處即可
'''