DAY17-認證系統

COOKIE 與 SESSION

概念

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}

知道了原理,下面就來用代碼實現。

瀏覽器查看cookie

咱們使用Chrome瀏覽器,打開開發者工具。

Django實現的COOKIE

一、獲取Cookie

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    #參數:
        default: 默認值
           salt: 加密鹽
        max_age: 後臺控制過時時間

二、設置Cookie

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。
max_age和expires區別

 

3 刪除cookie

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")
cookie版登陸

Django實現的SESSION

一、 基本操做

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})
Session版登陸驗證

二、流程解析圖

#(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/')
View Code

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>
View Code

四、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,默認修改以後才保存(默認)
View Code

(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,默認修改以後才保存
View Code

(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,默認修改以後才保存
View Code

其餘存儲方式

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,默認修改以後才保存(默認)
View Code

CBV中加裝飾器相關

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')
views.py

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()), 是固定用法。
View Code

要在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方法上

備註:

  • csrf_protect,爲當前函數強制設置防跨站請求僞造功能,即使settings中沒有設置全局中間件。
  • csrf_exempt,取消當前函數防跨站請求僞造功能,即使settings中設置了全局中間件。
    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') 

用戶認證

auth模塊

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中提供了許多方法,這裏主要介紹其中的三個:

1 、authenticate()   

提供了用戶認證,即驗證用戶名以及密碼是否正確,通常須要username  password兩個關鍵字參數

若是認證信息有效,會返回一個  User  對象。authenticate()會在User 對象上設置一個屬性標識那種認證後端認證了該用戶,且該信息在後面的登陸過程當中是須要的。當咱們試圖登錄一個從數據庫中直接取出來不通過authenticate()的User對象會報錯的!!

使用authenticate模塊,使用時,先導入模塊:

from django.contrib.auth import authenticate

user = authenticate(username='someone',password='somepassword')

 

須要注意的是:該方法不檢查is_active標誌位。

2 、login(HttpRequest, user)

該函數接受一個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  %}

3 、logout(request) 註銷用戶

from django.contrib.auth import logout
   
def logout_view(request):
  logout(request)
  # Redirect to a success page.

該函數接受一個HttpRequest對象,無返回值。當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。

4 、user對象的 is_authenticated()

要求:

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對象

User 對象屬性:username, password(必填項)password用哈希算法保存到數據庫

is_staff : 用戶是否擁有網站的管理權限.

is_active : 是否容許用戶登陸, 設置爲``False``,能夠不用刪除用戶來禁止 用戶登陸

2.1 、is_authenticated()

若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。
經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。 這個方法很重要, 在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name

.2 、建立用戶

使用 create_user 輔助函數建立用戶:

from django.contrib.auth.models import User
user = User.objects.create_user(username='',password='',email=''

2.3 、check_password(passwd)

用戶須要修改密碼的時候 首先要讓他輸入原來的密碼 ,若是給定的字符串經過了密碼檢查,返回 True

2.4 、修改密碼

使用 set_password() 來修改密碼

user = User.objects.get(username='')
user.set_password(password='')
user.save

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)  
View Code

修改密碼:

@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)
View Code

表繼承 

使用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

 

Group

django.contrib.auth.models.Group定義了用戶組的模型, 每一個用戶組擁有idname兩個字段, 該模型在數據庫被映射爲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()

Permission

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_idcodename四個字段。

管理用戶權限

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')
相關文章
相關標籤/搜索