0.概述
Django 有一個內置的授權系統。他用來處理用戶、分組、權限以及基於 cookie 的會話系統。 Django 的授權系統包括驗證和授權兩個部分。驗證是驗證這個用戶是否是他聲稱的人(比如用戶名和密碼驗證,角色驗證),授權是給與他相應的權限。 Django 內置的權限系統包括以下方面:
1. 用戶。 2. 權限。 3. 分組。 4. 一個可以配置的密碼哈希系統。 5. 一個可插拔的后台管理系統。
內置系統的使用:默認中創建完一個 django 項目后,其實就已經集成了授權系統。那哪些部分是跟授權系統相關的配置呢。以下做一個簡單列表
INSTALLED_APPS : 1. django.contrib.auth :包含了一個核心授權框架,以及大部分的模型定義。 2. django.contrib.contenttypes : Content Type 系統,可以用來關聯模型和權限。 中間件: 1. SessionMiddleware :用來管理 session 。 2. AuthenticationMiddleware :用來處理和當前 session 相關聯的用戶
1.關於認證與授權的本質
實質上是把用戶存在數據庫中的數據和通過表單提交上來的數據做對比,看是否相等。認證的作用是確定用戶的身份,比如是否是公司的員工,沒是否是注冊用戶。而權限作用確定用戶能夠做什么,比如用戶是否可以瀏覽付費咨詢,如果后台系統的話對於員工的話,是否可以修改編輯某些文件等。
2.django內置了驗證系統-----auth
(1)authenticate(傳入用戶名和密碼實現用戶的登錄,驗證成功后返回一個user對象)
(2)login(完成具體的登錄過程)
該函數接受一個HttpRequest對象 ,以及一個認證了的User對象(通過authenticate驗證的獨享),此函數使用django的session框架給某個已認證的用戶附加上session id等信息。(在響應頭里面對數據作出了修改)
(3)logout(退出登錄,本質上是封裝了函數,調用session的flush()方法,清楚了內容)
3.User對象(user模型是整個框架的核心):
(1)is_authenticated(),判斷用戶是否登錄(利用django的login_required裝飾器來操作,登陸成功后就返回到原來的訪問頁面(裝飾器實現的核心代碼)):
def my_view(request): if not request.user.is_authenticated(): return redirect("%s?next=%s"%(settings.LOGIN_URL, request.path))
(2)create_user與create_superuser,需要傳入喲郵箱,密碼和用戶名稱參數。
(3)checkpassword,setpassword(密碼通過hash算法加密后存儲在數據庫中)
4.django自定義User:
(1)利用proxy代理模式;------關於ORM模式的代理模式的使用。
適用條件:如果你對 Django 提供的字段,以及驗證的方法都比較滿意,沒有什么需要改的
應用:通過設置proxy=true來設置代理模式,應用場景(設置黑名單列表),優點是不影響原理的user表,可以擴展功能:
class Person(User): class Meta: proxy = True def get_blacklist(self): return self.objects.filter(is_active=False)
后續可以直接調用get_blacklist()獲取黑名單。
(2)利用一對一外鍵;
適用條件:對於用戶驗證方法authentica比較滿意,利用authenticate來做驗證,只是想要拓展字段。
應用:通過設置一對一外鍵,可以存在用戶表中的不常用的字段,放在擴展字段。想實現利用手機號或者用郵箱來熟悉愛你用戶認證登錄。
from django.contrib.auth.models import User from django.db import models from django.dispatch import receiver from django.db.models.signals import post_save class UserExtension(models.Model): user = models.OneToOneField(User,on_delete=models.CASCADE,related_name='extension') birthday = models.DateField(null=True,blank=True) school = models.CharField(max_length=100) @receiver(post_save,sender=User) def create_user_extension(sender,instance,created,**kwargs): if created: UserExtension.objects.create(user=instance) else: instance.extension.save
上面利用信號量機制,當user模型執行保存的時候,就會創建一個UserExtension對象與之綁定。問題:保存數據需要執行兩次的操作。
(3)繼承自AbstracUser
適用條件:想要修改authenticate驗證方式,在不改變原來字段的基礎上增加字段到模型中,通過繼承User的父類來生成新的字段。
from django.contrib.auth.models import AbstractUser class User(AbstractUser): telephone = models.CharField(max_length=11,unique=True) school = models.CharField(max_length=100) # 指定telephone作為USERNAME_FIELD,以后使用authenticate # 函數驗證的時候,就可以根據telephone來驗證 # 而不是原來的username USERNAME_FIELD = 'telephone' REQUIRED_FIELDS = [] # 重新定義Manager對象,在創建user的時候使用telephone和 # password,而不是使用username和password objects = UserManager()
class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self,telephone,password,**extra_fields): if not telephone: raise ValueError("請填入手機號碼!") user = self.model(telephone=telephone,*extra_fields) user.set_password(password) user.save() return user def create_user(self,telephone,password,**extra_fields): extra_fields.setdefault('is_superuser',False) return self._create_user(telephone,password)
def create_superuser(self,telephone,password,**extra_fields): extra_fields['is_superuser'] = True return self._create_user(telephone,password)
因為改變了原來的User需要在settings文件中重新做配置:AUTH_USER_MODEL=youapp.User。此外,因為這種方式,破壞了表結構,需要在第一次migrate之前先定義好。
否則會與系統內置的User發生沖突。
(4)繼承自Abstractbaseuser
適用條件:修改驗證方式,並且決定對原有的字段做修改,刪除一些字段。
應用:繼承自AbstractBaseUser,可以按照自己的方式來定義和選擇字段。
class User(AbstractBaseUser,PermissionsMixin): email = models.EmailField(unique=True) username = models.CharField(max_length=150) telephone = models.CharField(max_length=11,unique=True) is_active = models.BooleanField(default=True) # 用來描述 User 模型名字字段的字符串,作為唯一的標識,默認是 # username USERNAME_FIELD = 'telephone' 通過 createsuperuser 管理命令創建一個用戶時的提示。 REQUIRED_FIELDS = [] # 將修改后的模型管理類賦值給原理的類,后續調用(具體了解django的Manager類的使用) objects = UserManager() def get_full_name(self): return self.username def get_short_name(self): return self.username class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self,telephone,password,**extra_fields): if not telephone: raise ValueError("請填入手機號碼!") user = self.model(telephone=telephone,*extra_fields) user.set_password(password) user.save() return user def create_user(self,telephone,password,**extra_fields): extra_fields.setdefault('is_superuser',False) return self._create_user(telephone,password) def create_superuser(self,telephone,password,**extra_fields): extra_fields['is_superuser'] = True return self._create_user(telephone,password)
在創建了新的模型類后,需要seettins中配置好AUTH_USER_MODEL='appname.User'。后續如果想調用User,可以通過“settings.AUTH_USER_MODEL”.
5.權限
權限本質上是存在content_type表中的字段,在表中記錄了每個模型所具有的字段(permission)是模型級別的權限,針對表的操作作出限制。通過將用戶與權限關聯起來,權限判斷的本質就是查詢數據庫,看用戶是否具有某項權限。如果有權限就可以執行某個視圖,沒有,就無法執行該視圖,給用戶返回提示信息,無法執行該視圖。
(1)給用戶添加權限:
通過模型對象的方式來添加:
class Article(models.Model): title = models.CharField(max_length=100) content = models.TextField() author = models.ForeignKey(get_user_model(),on_delete=models.CASCADE) class Meta: permissions = ( ('view_article','can view article'), )
通過權限都是 django.contrib.auth.Permission 的實例。這個模型包含三個字段, name 、 codename 以及 content_type ,其中的 content_type 表示這個 permission 是屬於
哪個 app 下的哪個 models 。
from django.contrib.auth.models import Permission,ContentType from .models import Article content_type = ContentType.objects.get_for_model(Article) permission = Permission.objects.create(name='可以編輯的權限',codename='edit_article',con tent_type=content_type)
(2)權限本身只是一個數據,可以通過下面方法來實現對權限的綁定:
1. myuser.user_permissions.set(permission_list) :直接給定一個權限的列表。 2. myuser.user_permissions.add(permission,permission,...) :一個個添加權限。 3. myuser.user_permissions.remove(permission,permission,...) :一個個刪除權限。 4. myuser.user_permissions.clear() :清除權限。 5. myuser.has_perm('<app_name>.<codename>') :判斷是否擁有某個權限。權限參數是一個字符 串,格式是 app_name.codename 。 6. myuser.get_all_permissons() :獲取所有的權限
(3)權限限定裝飾器(使用 django.contrib.auth.decorators.permission_required 可以非常方便的檢查用戶是否擁有這個權限,如果擁有,那么就可以進入到指定的視圖函數中)
from django.contrib.auth.decorators import permission_required @permission_required('front.view_article') def my_view(request): ...
6.分組
為了方便對用戶進行批量管理,使用django.contrib.auth.models.Group 模型,每個用戶組擁有 id 和 name 兩個字段,該模型在數據庫被映射為 auth_group 數據表。
把一些權限歸類,然后添加到某個分組中,之后再把和把需要賦予這些權限的用戶添加到這個分組中。
(1)用戶分組操作:
1. Group.object.create(group_name) :創建分組。 2. group.permissions :某個分組上的權限。多對多的關系。 group.permissions.add :添加權限。 group.permissions.remove :移除權限。 group.permissions.clear :清除所有權限。 user.get_group_permissions() :獲取用戶所屬組的權限。 3. user.groups :某個用戶上的所有分組。多對多的關系。
(2)在模板中使用權限(在 settings.TEMPLATES.OPTIONS.context_processors 下,因為添加了 django.contrib.auth.context_processors.auth 上下文處理器,因此在模板中可以直接通過 perms 來獲取用戶的所有權限。)
{% if perms.front.add_article %} <a href='/article/add/'>添加文章</a> {% endif %}
7.總結:
一個好的程序員不應該只是知道不要重復造輪子,但是應該知道輪子是如何造的,了解一個功能背后的原理,能夠設計出適合自己的輪子,這才是優秀的程序員。所謂舉一反三,從設計者的角度來思考。要知道代碼只是結局問題的一種表達方式,你可以有自己的表達方式。
在django框架中,ORM模型是核心,授權與驗證系統實質上就基於ORM模型來操作。整個系統本質上是對數據庫中表的操作,包括以下幾個模型(紅色框內的):
上述模型,分別存儲了用戶,權限和組以及組權限的信息。而對於模型的操作,則通過對模型類定義相應的操作方法(本質上是發呢告狀了ORM對數據庫的操作語句),使用的時候直接調用封裝后的函數,制定對模型的操作。
通過上述分析,了解了auth認證系統的作用,方便直接使用,同時對於其內部原理有了一個簡單的了解,可以利用面向對象的特性,如同對於User模型進行擴展,可以對整個模型模型進行拓展。甚至,可以開發出自己的驗證授權系統。
最后附上兩個文章,是flask系統中如何自己編寫權限認證,如果你熟悉django的權限認證系統,完全可以自己寫出一個來使用,使用輪子是為了能造出更好的輪子:
8.補充
django的permission是表級別的權限,如果在某些特殊情況下,需要執行行級別的數據,咋可以使用django-guardian,參照下面博客:
https://blog.csdn.net/scdxmoe/article/details/72678005