您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Django中內置權限擴展的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
背景介紹
overmind項目使用了Django內置的權限系統,Django內置權限系統基于model層做控制,新的model創建后會默認新建三個權限,分別為:add、change、delete,如果給用戶或組賦予delete的權限,那么用戶將可以刪除這個model下的所有數據。
原本overmind只管理了我們自己部門的數據庫,權限設置只針對具體的功能不針對細粒度的數據庫實例,例如用戶A 有審核的權限,那么用戶A 可以審核所有的DB,此時使用內置的權限系統就可以滿足需求了,但隨著系統的不斷完善要接入其他部門的數據庫管理,這就要求針對不同用戶開放不同DB的權限了,例如A部門的用戶只能操作A部門的DB,Django內置基于model的權限無法滿足需求了。
實現過程
先來確定下需求:
1. 保持原本的基于功能的權限控制不變,例如用戶A有查詢權限,B有審核權限
2. 增加針對DB實例的權限控制,例如用戶A只能查詢特定的DB,B只能審核特定的DB
對于上邊需求1用內置的權限系統已經可以實現,這里不贅述,重點看下需求2,DB信息都存放在同一個表里,不同用戶能操作不同的DB,也就是需要把每一條DB信息與有權限操作的用戶進行關聯,為了方便操作,我們考慮把DB跟用戶組關聯,在用戶組里的用戶都有權限,而操作類型經過分析主要有兩類讀和寫,那么需要給每個MySQL實例添加兩個字段分別記錄對此實例有讀和寫權限的用戶組
如下代碼在原來的model基礎上添加 read_groups
和 write_groups
字段,DB實例跟用戶組應是ManyToManyField多對多關系,一個實例可以關聯多個用戶組,一個用戶組也可以屬于多個實例
class Mysql(models.Model): Env = ( (1, 'Dev'), (2, 'Qa'), (3, 'Prod'), ) create_time = models.DateTimeField(auto_now_add=True, verbose_name='創建時間') update_time = models.DateTimeField(auto_now=True, verbose_name='更新時間') project_id = models.IntegerField(verbose_name='項目') project_tmp = models.CharField(max_length=128, default='') environment = models.IntegerField(choices=Env, verbose_name='環境') master_host = models.GenericIPAddressField(verbose_name='master主機') master_port = models.IntegerField(default=3306, verbose_name='master端口') slave_host = models.GenericIPAddressField(null=True, verbose_name='slave主機') slave_port = models.IntegerField(null=True, default=3306, verbose_name='slave端口') database = models.CharField(max_length=64, verbose_name='數據庫') read_groups = models.ManyToManyField(Group, related_name='read', verbose_name='讀權限') write_groups = models.ManyToManyField(Group, related_name='write', verbose_name='寫權限') description = models.TextField(null=True, verbose_name='備注')
model確定了,接下來我們分三部分詳細介紹下權限驗證的具體實現
列表頁權限控制
如上圖列表頁,每個用戶進入系統后只能查看自己有讀權限的MySQL實例列表,管理員能查看所有,代碼如下:
def mysql(request): if request.method == 'GET': if request.user.is_superuser: _lists = Mysql.objects.all().order_by('id') else: # 獲取登錄用戶的所有組 _user_groups = request.user.groups.all() # 構造一個空的QuerySet然后合并 _lists = Mysql.objects.none() for group in _user_groups: _lists = _lists | group.read.all() return render(request, 'overmind/mysql.index.html', {'request': request, 'lPage': _lists})
實現的思路是:獲取登錄用戶的所有組,然后循環查詢每個組有讀取權限的數據庫實例,最后把每個組有權限讀的數據庫實例進行合并返回
獲取登錄用戶的所有組用到了ManyToMany的查詢方法: request.user.groups.all()
最終返回的一個結果是QuerySet,所以我們需要先構造一個空的Queryset: Mysql.objects.none()
QuerySet合并不能用簡單的相加,應為: QuerySet-1 | QuerySet-2
查詢接口權限控制
如上圖系統中有很多功能是需要根據項目、環境查詢對應的DB信息的,對于此類接口也需要控制用戶只能查詢自己有權限讀的DB實例,管理員能查看所有,代碼如下:
def get_project_database(request, project, environment): if request.method == 'GET': _jsondata = {} if request.user.is_superuser: # 返回所有項目和環境匹配的DB _lists = Mysql.objects.filter( project_id=int(project), environment=int(environment) ) _jsondata = {i.id: i.database for i in _lists} else: # 只返回用戶有權限查詢的DB _user_groups = request.user.groups.all() for group in _user_groups: # 循環mysql表中有read_groups權限的所有組 for mysql in group.read.all(): if mysql.project_id == int(project) and mysql.environment == int(environment): _jsondata[mysql.id] = mysql.database return JsonResponse(_jsondata)
實現思路與上邊類似,只是多了一步根據項目和環境再進行判斷
需要根據group去反查都有哪些DB實例包含了該組,這里用到了M2M的related_name屬性: group.read.all()
更多關于Django ORM查詢的內容可以看這篇文章 Django model select的各種用法詳解 有詳細的總結
執行操作權限控制
除了上邊的兩個場景之外我們還需要在執行具體的操作之前去判斷是否有權限,例如執行審核操作前判斷用戶是否對此DB有寫權限
有很多地方都需要做這個判斷,所以把這個權限判斷單獨寫個方法來處理,代碼如下:
def check_permission(perm, mysql, user): # 如果用戶是超級管理員則有權限 if user.is_superuser: return True # 取出用戶所屬的所有組 _user_groups = user.groups.all() # 取出Mysql對應權限的所有組 if perm == 'read': _mysql_groups = mysql.read_groups.all() if perm == 'write': _mysql_groups = mysql.write_groups.all() # 用戶組和DB權限組取交集,有則表示有權限,否則沒有權限 group_list = list(set(_user_groups).intersection(set(_mysql_groups))) return False if len(group_list) == 0 else True
實現思路是:根據傳入的第三個用戶參數,來獲取到用戶所有的組,然后根據傳入的第一個參數類型讀取或寫入和第二個參數DB實例來獲取到有權限的所有組,然后對兩個組取交集,交集不為空則表示有權限,為空則沒有
M2M的 .all()
取出來的結果是個list,兩個list取交集的方法為: list(set(list-A).intersection(set(list-B)))
view中使用就很簡單了,如下:
def query(request): if request.method == 'POST': postdata = request.body.decode('utf-8') _host = get_object_or_404(Mysql, id=int(postdata.get('database'))) # 檢查用戶是否有DB的查詢權限 if check_permission('read', _host, request.user) == False: return JsonResponse({'state': 0, 'message': '當前用戶沒有查詢此DB的權限'})
關于“Django中內置權限擴展的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。