cookie不屬於http協議範圍,因爲http協議沒法保持狀態,但實際狀況,咱們卻又須要「保持狀態」,所以cookie就是在這樣一個場景下誕生。javascript
cookie的工做原理是:由服務器產生內容,瀏覽器收到請求後保存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上cookie,這樣服務器就能經過cookie的內容來判斷這個是「誰」了。html
cookie雖然在必定程度上解決了「保持狀態」的需求,可是因爲cookie自己最大支持4096字節,以及cookie自己保存在客戶端,可能被攔截或竊取,所以就須要有一種新的東西,它能支持更多的字節,而且他保存在服務器,有較高的安全性。這就是session。java
問題來了,基於http協議的無狀態特徵,服務器根本就不知道訪問者是「誰」。那麼上述的cookie就起到橋接的做用。jquery
咱們能夠給每一個客戶端的cookie分配一個惟一的id,這樣用戶在訪問時,經過cookie,服務器就知道來的人是「誰」。而後咱們再根據不一樣的cookie的id,在服務器上保存一段時間的私密資料,如「帳號密碼」等等。程序員
總結而言:cookie彌補了http無狀態的不足,讓服務器知道來的人是「誰」;可是cookie以文本的形式保存在本地,自身安全性較差;因此咱們就經過cookie識別不一樣的用戶,對應的在session裏保存私密的信息以及超過4096字節的文本。ajax
另外,上述所說的cookie和session實際上是共通性的東西,不限於語言和框架算法
前幾節的介紹中咱們已經有能力製做一個登錄頁面,在驗證了用戶名和密碼的正確性後跳轉到後臺的頁面。可是測試後也發現,若是繞過登錄頁面。直接輸入後臺的url地址也能夠直接訪問的。這個顯然是不合理的。其實咱們缺失的就是cookie和session配合的驗證。有了這個驗證過程,咱們就能夠實現和其餘網站同樣必須登陸才能進入後臺頁面了。數據庫
先說一下這種認證的機制。每當咱們使用一款瀏覽器訪問一個登錄頁面的時候,一旦咱們經過了認證。服務器端就會發送一組隨機惟一的字符串(假設是123abc)到瀏覽器端,這個被存儲在瀏覽端的東西就叫cookie。而服務器端也會本身存儲一下用戶當前的狀態,好比login=true,username=hahaha之類的用戶信息。可是這種存儲是以字典形式存儲的,字典的惟一key就是剛纔發給用戶的惟一的cookie值。那麼若是在服務器端查看session信息的話,理論上就會看到以下樣子的字典django
{'123abc':{'login':true,'username:hahaha'}}後端
由於每一個cookie都是惟一的,因此咱們在電腦上換個瀏覽器再登錄同一個網站也須要再次驗證。那麼爲何說咱們只是理論上看到這樣子的字典呢?由於處於安全性的考慮,其實對於上面那個大字典不光key值123abc是被加密的,value值{'login':true,'username:hahaha'}在服務器端也是同樣被加密的。因此咱們服務器上就算打開session信息看到的也是相似與如下樣子的東西
{'123abc':dasdasdasd1231231da1231231}
知道了原理,下面就來用代碼實現。
咱們使用Chrome瀏覽器,打開開發者工具。
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) #參數: default: 默認值 salt: 加密鹽 max_age: 後臺控制過時時間
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密鹽',...)
參數:
''' def set_cookie(self, key, 鍵 value='', 值 max_age=None, 超長時間 expires=None, 超長時間 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獲取 (不是絕對,底層抓包能夠獲取到也能夠被覆蓋) ): pass '''
因爲cookie保存在客戶端的電腦上,因此,JavaScript和jquery也能夠操做cookie。
<script src='/static/js/jquery.cookie.js'> </script> $.cookie("key", value,{ path: '/' });
Expires和max-age均可以用來指定文檔的過時時間,可是兩者有一些細微差異 1.Expires在HTTP/1.0中已經定義,Cache-Control:max-age在HTTP/1.1中才有定義,爲了向下兼容,僅使用max-age不夠; 2.Expires指定一個絕對的過時時間(GMT格式),這麼作會致使至少2個問題1)客戶端和服務器時間不一樣步致使Expires的配置出現問題 2)很容易在配置後忘記具體的過時時間,致使過時來臨出現浪涌現象; 3.max-age 指定的是從文檔被訪問後的存活時間,這個時間是個相對值(好比:3600s),相對的是文檔第一次被請求時服務器記錄的Request_time(請求時間) 4.Expires指定的時間能夠是相對文件的最後訪問時間(Atime)或者修改時間(MTime),而max-age相對對的是文檔的請求時間(Atime) 5.在Apache中,max-age是根據Expires的時間來計算出來的max-age = expires- request_time:(mod_expires.c) 429 expires = base + additional; 430 apr_table_mergen(t, "Cache-Control", 431 apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT, 432 apr_time_sec(expires - r->request_time))); 注:若是是A,base=request_time,M的話base=finfo.mtime. expires - request_time獲得max-age,若是Expires根據的是A(也就是AccessTime)設置的(A後面的值就是addtional),那麼expires設置的值就等於addtional值,可是若是Expires根據的是Mtime,那麼若是M後面的參數小於最後修改時間到當前時間的差(比方說2小時前修改了文件(19:00:00),如今設置M3600(如今是21:00:00)而且訪問,max-age=expires-request_time = (finfo.mtime+additional)- request_time),計算出的max-age就是負數(能夠試驗看到這個結果): max-age = (19:00:00+3600/3600) - 21:00:00 = -3600s 這樣,在Apache中,max-age就不只僅是相對Atime的時間了,若是設置爲M,相對的是Mtime。
response.delete_cookie("cookie_key",path="/",domain=name)
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 刪除用戶瀏覽器上以前設置的usercookie值 return rep
Cookie版登錄校驗
def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.get_signed_cookie("login", salt="SSS", default=None) == "yes": # 已經登陸的用戶... return func(request, *args, **kwargs) else: # 沒有登陸的用戶,跳轉剛到登陸頁面 return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": username = request.POST.get("username") passwd = request.POST.get("password") if username == "xxx" and passwd == "dashabi": next_url = request.GET.get("next") if next_url and next_url != "/logout/": response = redirect(next_url) else: response = redirect("/class_list/") response.set_signed_cookie("login", "yes", salt="SSS") return response return render(request, "login.html")
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 :
五、get(key, default=None) fav_color = request.session.get('fav_color', 'red') #不存在則賦值red 6、pop(key) fav_color = request.session.pop('fav_color') 七、全部 鍵、值、鍵值對
request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() 8、setdefault()
request.session.setdefault('k1',123) # 存在則不設置 9、flush() 刪除當前的會話數據並刪除會話的Cookie。 這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問 例如,django.contrib.auth.logout() 函數中就會調用它。 十、 # 用戶session的隨機字符串 request.session.session_key # 將全部Session失效日期小於當前日期的數據刪除 request.session.clear_expired() # 檢查 用戶session的隨機字符串 在數據庫中是否 request.session.exists("session_key") # 刪除當前用戶的全部Session數據 request.session.delete("session_key") request.session.set_expiry(value) * 若是value是個整數,session會在些秒數後失效。 * 若是value是個datatime或timedelta,session就會在這個時間後失效。 * 若是value是0,用戶關閉瀏覽器session就會失效。 * 若是value是None,session會依賴全局session失效策略。
Session版登錄驗證
from functools import wraps def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.session.get("user"): return func(request, *args, **kwargs) else: return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": user = request.POST.get("user") pwd = request.POST.get("pwd") if user == "alex" and pwd == "alex1234": # 設置session request.session["user"] = user # 獲取跳到登錄頁面以前的URL next_url = request.GET.get("next") # 若是有,就跳轉回登錄以前的URL if next_url: return redirect(next_url) # 不然默認跳轉到index頁面 else: return redirect("/index/") return render(request, "login.html") @check_login def logout(request): # 刪除全部當前請求相關的session request.session.delete() return redirect("/login/") @check_login def index(request): current_user = request.session.get("user", None) return render(request, "index.html", {"user": current_user})
#(3)設置session時進行的操做 request.session["user_id"]=user.pk request.session["username"]=user.user ''' 判斷瀏覽器以前是否有cookie,若是有則更新這個session_key,一個瀏覽器只能維護一個session_key,也就是一個cookie的value。 if request.COOKIE.get("sessionid"): 更新 else: {"user_id":1,"username":"alex"} 第一步: 生成隨機字符串: vwerascxh24asdasdasdsd 第二步: 在django-sesion表生成一條記錄: session-key session-data vwerascxh24asdasdasdsd {"user_id":1,"username":"alex"} 第三步: obj.set_cookie("sessionid",vwerascxh24asdasdasdsd) ''' #(4)查詢一個session時的操做 request.session.get("user_id") ''' 1 request.COOKIE.get("sessionid"):vwerascxh24asdasdasdsd 2 在django-sesion表查詢一條記錄:session-key=vwerascxh24asdasdasdsd 3 session-data({"user_id":1,"username":"alex"}).get("user_id") '''
views:
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/')
template:
===================================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)數據庫配置(默認):
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,默認修改以後才保存(默認)
(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' # 引擎 其餘公用設置項: 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,默認修改以後才保存(默認)
CBV(class base views) 就是在視圖裏使用類處理請求。
CBV實現的登陸視圖
class LoginView(View): def get(self, request): """ 處理GET請求 """ return render(request, 'login.html') def post(self, request): """ 處理POST請求 """ user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'alex' and pwd == "alex1234": next_url = request.GET.get("next") # 生成隨機字符串 # 寫瀏覽器cookie -> session_id: 隨機字符串 # 寫到服務端session: # { # "隨機字符串": {'user':'alex'} # } request.session['user'] = user if next_url: return redirect(next_url) else: return redirect('/index/') return render(request, 'login.html')
urls.py須要修改以下:
from app01 import views urlpatterns = [ # url(r‘^index/‘, views.index), url(r‘^login/‘, views.LoginView.as_view()), ] #注:url(r‘^login/‘, views.LoginView.as_view()), 是固定用法。
要在CBV視圖中使用咱們上面的check_login裝飾器,有如下三種方式:
from django.utils.decorators import method_decorator
1. 加在CBV視圖的get或post方法上
from django.utils.decorators import method_decorator class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") @method_decorator(check_login) def post(self, request): print("Home View POST method...") return redirect("/index/")
2. 加在dispatch方法上
from django.utils.decorators import method_decorator class HomeView(View): @method_decorator(check_login) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
由於CBV中首先執行的就是dispatch方法,因此這麼寫至關於給get和post方法都加上了登陸校驗。
3. 直接加在視圖類上,但method_decorator必須傳 name 關鍵字參數
若是get方法和post方法都須要登陸校驗的話就寫兩個裝飾器。
from django.utils.decorators import method_decorator @method_decorator(check_login, name="get") @method_decorator(check_login, name="post") class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
CSRF Token相關裝飾器在CBV只能加到dispatch方法上
備註:
from django.views.decorators.csrf import csrf_exempt, csrf_protect class HomeView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
CSRF Token相關裝飾器在FBV就直接加在函數上:
from django.views.decorators.csrf import csrf_exempt def login(request): print('hello ajax') return render(request,'index.html') @csrf_exempt #csrf防護 def ajax_post(request): print('ok') return HttpResponse('helloyuanhao')
from django.contrib import auth
User表auth_user的SQL描述:
CREATE TABLE "auth_user" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "password" varchar(128) NOT NULL, "last_login" datetime NULL, "is_superuser" bool NOT NULL, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL, "email" varchar(254) NOT NULL, "is_staff" bool NOT NULL, "is_active" bool NOT NULL, "date_joined" datetime NOT NULL, "username" varchar(30) NOT NULL UNIQUE )
django.contrib.auth中提供了許多方法,這裏主要介紹其中的三個:
提供了用戶認證,即驗證用戶名以及密碼是否正確,通常須要username password兩個關鍵字參數
若是認證信息有效,會返回一個 User 對象。authenticate()會在User 對象上設置一個屬性標識那種認證後端認證了該用戶,且該信息在後面的登陸過程當中是須要的。當咱們試圖登錄一個從數據庫中直接取出來不通過authenticate()的User對象會報錯的!!
使用authenticate
模塊,使用時,先導入模塊:
from django.contrib.auth import authenticate user = authenticate(username='someone',password='somepassword')
須要注意的是:該方法不檢查is_active
標誌位。
該函數接受一個HttpRequest對象,以及一個認證了的User對象
此函數使用django的session框架給某個已認證的用戶附加上session id等信息。
login不進行認證,也不檢查is_active標誌位, 通常和authenticate配合使用:
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. ...
登陸過的用戶,能夠在views的其餘函數中直接調用request.user獲得登陸用戶對象,甚至能夠在模板中直接使用{% request.user.username %};
若是用戶未登陸,則request.user也有一個對象,是一個匿名用戶對象,其全部屬性如用戶名,密碼等都爲空,因此在模板中驗證用戶是否登陸須要用
{% if request.user.username %} 而不是{% if request.user %}
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
該函數接受一個HttpRequest對象,無返回值。當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。
要求:
1 用戶登錄後才能訪問某些頁面,
2 若是用戶沒有登陸就訪問該頁面的話直接跳到登陸頁面
3 用戶在跳轉的登錄界面中完成登錄後,自動訪問跳轉到以前訪問的地址
方法1:
def my_view(request): if not request.user.is_authenticated(): return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
方法2:
django已經爲咱們設計好了一個用於此種狀況的裝飾器:login_requierd()
from django.contrib.auth.decorators import login_required @login_required(login_url='/accounts/login/') def my_view(request):
...
未登陸用戶將被重定向到login_url
指定的位置。若未指定login_url參數, 則會跳轉到django默認的 登陸URL '/accounts/login/ ' (這個值能夠在settings文件中經過LOGIN_URL進行修改)。並傳遞 當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。
User 對象屬性:username, password(必填項)password用哈希算法保存到數據庫
is_staff : 用戶是否擁有網站的管理權限.
is_active : 是否容許用戶登陸, 設置爲``False``,能夠不用刪除用戶來禁止 用戶登陸
若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。
經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。 這個方法很重要, 在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name
使用 create_user 輔助函數建立用戶:
from django.contrib.auth.models import User user = User.objects.create_user(username='',password='',email='')
用戶須要修改密碼的時候 首先要讓他輸入原來的密碼 ,若是給定的字符串經過了密碼檢查,返回 True
使用 set_password() 來修改密碼
user = User.objects.get(username='') user.set_password(password='') user.save
註冊:
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)
使用auth模塊大前提是要使用Django自動生成的用戶表:auth_user,然而這個表字段是已定義好的,缺少靈活性,可使用表繼承功能爲其添加自定義字段;
繼承表後,將繼承表和父表合二爲一,繼承表擁有父表全部字段:
from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): #繼承自帶user表 """ 用戶信息 """ nid = models.AutoField(primary_key=True) #本身重寫主鍵會覆蓋原主鍵 telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True) def __str__(self): return self.username
settings.py
#找合適位置添加: AUTH_USER_MODEL="blog.UserInfo" #blog是應用名.UserInfo是表名
views.py
#使用繼承擴展user表後,在使用User功能時須要修改以下 # from django.contrib.auth.models import User from .models import UserInfo as User
django.contrib.auth.models.Group
定義了用戶組的模型, 每一個用戶組擁有id
和name
兩個字段, 該模型在數據庫被映射爲auth_group
數據表。
User對象中有一個名爲groups
的多對多字段, 多對多關係由auth_user_groups
數據表維護。Group對象能夠經過user_set
反向查詢用戶組中的用戶。
咱們能夠經過建立刪除Group對象來添加或刪除用戶組:
# add group = Group.objects.create(name=group_name) group.save() # del group.delete()
咱們能夠經過標準的多對多字段操做管理用戶與用戶組的關係:
#用戶加入用戶組 user.groups.add(group) #或者 group.user_set.add(user) #用戶退出用戶組 user.groups.remove(group) #或者 group.user_set.remove(user) #用戶退出全部用戶組 user.groups.clear() #用戶組中全部用戶退出組 group.user_set.clear()
Django的auth系統提供了模型級的權限控制, 便可以檢查用戶是否對某個數據表擁有增(add), 改(change), 刪(delete)權限。
auth系統沒法提供對象級的權限控制, 即檢查用戶是否對數據表中某條記錄擁有增改刪的權限。若是須要對象級權限控制可使用django-guardian
。
假設在博客系統中有一張article數據表管理博文, auth能夠檢查某個用戶是否擁有對全部博文的管理權限, 但沒法檢查用戶對某一篇博文是否擁有管理權限。
user.has_perm
方法用於檢查用戶是否擁有操做某個模型的權限:
user.has_perm('blog.add_article') user.has_perm('blog.change_article') user.has_perm('blog.delete_article')
述語句檢查用戶是否擁有blog這個app中article模型的添加權限, 若擁有權限則返回True。
has_perm
僅是進行權限檢查, 便是用戶沒有權限它也不會阻止程序員執行相關操做。
@permission_required
裝飾器能夠代替has_perm
並在用戶沒有相應權限時重定向到登陸頁或者拋出異常。
# permission_required(perm[, login_url=None, raise_exception=False]) @permission_required('blog.add_article') def post_article(request): pass
每一個模型默認擁有增(add), 改(change), 刪(delete)權限。在django.contrib.auth.models.Permission
模型中保存了項目中全部權限。
該模型在數據庫中被保存爲auth_permission
數據表。每條權限擁有id
,name
, content_type_id
, codename
四個字段。
User和Permission經過多對多字段user.user_permissions
關聯,在數據庫中由auth_user_user_permissions
數據表維護。
#添加權限 user.user_permissions.add(permission) #刪除權限: user.user_permissions.delete(permission) #清空權限: user.user_permissions.clear()
用戶擁有他所在用戶組的權限, 使用用戶組管理權限是一個更方便的方法。Group中包含多對多字段permissions
, 在數據庫中由auth_group_permissions
數據表維護。
#添加權限: group.permissions.add(permission) #刪除權限: group.permissions.delete(permission) #清空權限: group.permissions.clear()
在定義Model時可使用Meta自定義權限:
class Discussion(models.Model): ... class Meta: permissions = ( ("create_discussion", "Can create a discussion"), ("reply_discussion", "Can reply discussion"), )
判斷用戶是否擁有自定義權限:
user.has_perm('blog.create_discussion')