日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Django内置权限扩展案例

發(fā)布時(shí)間:2023/12/18 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Django内置权限扩展案例 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

當(dāng)Django的內(nèi)置權(quán)限無(wú)法滿(mǎn)足需求的時(shí)候就自己擴(kuò)展吧~

背景介紹

overmind項(xiàng)目使用了Django內(nèi)置的權(quán)限系統(tǒng),Django內(nèi)置權(quán)限系統(tǒng)基于model層做控制,新的model創(chuàng)建后會(huì)默認(rèn)新建三個(gè)權(quán)限,分別為:add、change、delete,如果給用戶(hù)或組賦予delete的權(quán)限,那么用戶(hù)將可以刪除這個(gè)model下的所有數(shù)據(jù)。

原本overmind只管理了我們自己部門(mén)的數(shù)據(jù)庫(kù),權(quán)限設(shè)置只針對(duì)具體的功能不針對(duì)細(xì)粒度的數(shù)據(jù)庫(kù)實(shí)例,例如用戶(hù)A 有審核的權(quán)限,那么用戶(hù)A 可以審核所有的DB,此時(shí)使用內(nèi)置的權(quán)限系統(tǒng)就可以滿(mǎn)足需求了,但隨著系統(tǒng)的不斷完善要接入其他部門(mén)的數(shù)據(jù)庫(kù)管理,這就要求針對(duì)不同用戶(hù)開(kāi)放不同DB的權(quán)限了,例如A部門(mén)的用戶(hù)只能操作A部門(mén)的DB,Django內(nèi)置基于model的權(quán)限無(wú)法滿(mǎn)足需求了。

實(shí)現(xiàn)過(guò)程

先來(lái)確定下需求:

  • 保持原本的基于功能的權(quán)限控制不變,例如用戶(hù)A有查詢(xún)權(quán)限,B有審核權(quán)限
  • 增加針對(duì)DB實(shí)例的權(quán)限控制,例如用戶(hù)A只能查詢(xún)特定的DB,B只能審核特定的DB
  • 對(duì)于上邊需求1用內(nèi)置的權(quán)限系統(tǒng)已經(jīng)可以實(shí)現(xiàn),這里不贅述,重點(diǎn)看下需求2,DB信息都存放在同一個(gè)表里,不同用戶(hù)能操作不同的DB,也就是需要把每一條DB信息與有權(quán)限操作的用戶(hù)進(jìn)行關(guān)聯(lián),為了方便操作,我們考慮把DB跟用戶(hù)組關(guān)聯(lián),在用戶(hù)組里的用戶(hù)都有權(quán)限,而操作類(lèi)型經(jīng)過(guò)分析主要有兩類(lèi)讀和寫(xiě),那么需要給每個(gè)MySQL實(shí)例添加兩個(gè)字段分別記錄對(duì)此實(shí)例有讀和寫(xiě)權(quán)限的用戶(hù)組

    如下代碼在原來(lái)的model基礎(chǔ)上添加read_groups和write_groups字段,DB實(shí)例跟用戶(hù)組應(yīng)是ManyToManyField多對(duì)多關(guān)系,一個(gè)實(shí)例可以關(guān)聯(lián)多個(gè)用戶(hù)組,一個(gè)用戶(hù)組也可以屬于多個(gè)實(shí)例

    class Mysql(models.Model):Env = ((1, 'Dev'),(2, 'Qa'),(3, 'Prod'),)create_time = models.DateTimeField(auto_now_add=True, verbose_name='創(chuàng)建時(shí)間')update_time = models.DateTimeField(auto_now=True, verbose_name='更新時(shí)間')project_id = models.IntegerField(verbose_name='項(xiàng)目')project_tmp = models.CharField(max_length=128, default='')environment = models.IntegerField(choices=Env, verbose_name='環(huán)境')master_host = models.GenericIPAddressField(verbose_name='master主機(jī)')master_port = models.IntegerField(default=3306, verbose_name='master端口')slave_host = models.GenericIPAddressField(null=True, verbose_name='slave主機(jī)')slave_port = models.IntegerField(null=True, default=3306, verbose_name='slave端口')database = models.CharField(max_length=64, verbose_name='數(shù)據(jù)庫(kù)')read_groups = models.ManyToManyField(Group, related_name='read', verbose_name='讀權(quán)限')write_groups = models.ManyToManyField(Group, related_name='write', verbose_name='寫(xiě)權(quán)限')description = models.TextField(null=True, verbose_name='備注')

    model確定了,接下來(lái)我們分三部分詳細(xì)介紹下權(quán)限驗(yàn)證的具體實(shí)現(xiàn)

    列表頁(yè)權(quán)限控制

    如上圖列表頁(yè),每個(gè)用戶(hù)進(jìn)入系統(tǒng)后只能查看自己有讀權(quán)限的MySQL實(shí)例列表,管理員能查看所有,代碼如下:

    def mysql(request):if request.method == 'GET':if request.user.is_superuser:_lists = Mysql.objects.all().order_by('id')else:# 獲取登錄用戶(hù)的所有組_user_groups = request.user.groups.all()# 構(gòu)造一個(gè)空的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})

    實(shí)現(xiàn)的思路是:獲取登錄用戶(hù)的所有組,然后循環(huán)查詢(xún)每個(gè)組有讀取權(quán)限的數(shù)據(jù)庫(kù)實(shí)例,最后把每個(gè)組有權(quán)限讀的數(shù)據(jù)庫(kù)實(shí)例進(jìn)行合并返回

    獲取登錄用戶(hù)的所有組用到了ManyToMany的查詢(xún)方法:request.user.groups.all()

    最終返回的一個(gè)結(jié)果是QuerySet,所以我們需要先構(gòu)造一個(gè)空的Queryset:Mysql.objects.none()

    QuerySet合并不能用簡(jiǎn)單的相加,應(yīng)為:QuerySet-1 | QuerySet-2

    查詢(xún)接口權(quán)限控制

    如上圖系統(tǒng)中有很多功能是需要根據(jù)項(xiàng)目、環(huán)境查詢(xún)對(duì)應(yīng)的DB信息的,對(duì)于此類(lèi)接口也需要控制用戶(hù)只能查詢(xún)自己有權(quán)限讀的DB實(shí)例,管理員能查看所有,代碼如下:

    def get_project_database(request, project, environment):if request.method == 'GET':_jsondata = {}if request.user.is_superuser:# 返回所有項(xiàng)目和環(huán)境匹配的DB_lists = Mysql.objects.filter(project_id=int(project),environment=int(environment))_jsondata = {i.id: i.database for i in _lists}else:# 只返回用戶(hù)有權(quán)限查詢(xún)的DB_user_groups = request.user.groups.all()for group in _user_groups:# 循環(huán)mysql表中有read_groups權(quán)限的所有組for mysql in group.read.all():if mysql.project_id == int(project) and mysql.environment == int(environment):_jsondata[mysql.id] = mysql.databasereturn JsonResponse(_jsondata)

    實(shí)現(xiàn)思路與上邊類(lèi)似,只是多了一步根據(jù)項(xiàng)目和環(huán)境再進(jìn)行判斷

    需要根據(jù)group去反查都有哪些DB實(shí)例包含了該組,這里用到了M2M的related_name屬性:group.read.all()

    更多關(guān)于Django ORM查詢(xún)的內(nèi)容可以看這篇文章Django model select的各種用法詳解有詳細(xì)的總結(jié)

    執(zhí)行操作權(quán)限控制

    除了上邊的兩個(gè)場(chǎng)景之外我們還需要在執(zhí)行具體的操作之前去判斷是否有權(quán)限,例如執(zhí)行審核操作前判斷用戶(hù)是否對(duì)此DB有寫(xiě)權(quán)限

    有很多地方都需要做這個(gè)判斷,所以把這個(gè)權(quán)限判斷單獨(dú)寫(xiě)個(gè)方法來(lái)處理,代碼如下:

    def check_permission(perm, mysql, user):# 如果用戶(hù)是超級(jí)管理員則有權(quán)限if user.is_superuser:return True# 取出用戶(hù)所屬的所有組_user_groups = user.groups.all()# 取出Mysql對(duì)應(yīng)權(quán)限的所有組if perm == 'read':_mysql_groups = mysql.read_groups.all()if perm == 'write':_mysql_groups = mysql.write_groups.all()# 用戶(hù)組和DB權(quán)限組取交集,有則表示有權(quán)限,否則沒(méi)有權(quán)限group_list = list(set(_user_groups).intersection(set(_mysql_groups)))return False if len(group_list) == 0 else True

    實(shí)現(xiàn)思路是:根據(jù)傳入的第三個(gè)用戶(hù)參數(shù),來(lái)獲取到用戶(hù)所有的組,然后根據(jù)傳入的第一個(gè)參數(shù)類(lèi)型讀取或?qū)懭牒偷诙€(gè)參數(shù)DB實(shí)例來(lái)獲取到有權(quán)限的所有組,然后對(duì)兩個(gè)組取交集,交集不為空則表示有權(quán)限,為空則沒(méi)有

    M2M的.all()取出來(lái)的結(jié)果是個(gè)list,兩個(gè)list取交集的方法為:list(set(list-A).intersection(set(list-B)))

    view中使用就很簡(jiǎn)單了,如下:

    def query(request):if request.method == 'POST':postdata = request.body.decode('utf-8')_host = get_object_or_404(Mysql, id=int(postdata.get('database')))# 檢查用戶(hù)是否有DB的查詢(xún)權(quán)限if check_permission('read', _host, request.user) == False:return JsonResponse({'state': 0, 'message': '當(dāng)前用戶(hù)沒(méi)有查詢(xún)此DB的權(quán)限'})

    寫(xiě)在最后

  • Django有第三方的基于object的權(quán)限管理模塊Django-guardian,本項(xiàng)目沒(méi)有使用主要是因?yàn)橐粊?lái)權(quán)限需求并不復(fù)雜,自己實(shí)現(xiàn)也很方便,二來(lái)個(gè)人在非必要的情況下并不喜歡引用過(guò)多第三方的包,后續(xù)升級(jí)維護(hù)都是負(fù)擔(dān)
  • 方案和代碼不盡完美,各位有更好的方案建議或更優(yōu)雅的代碼寫(xiě)法歡迎與我交流

  • 相關(guān)文章推薦閱讀:

    • Django+JWT實(shí)現(xiàn)Token認(rèn)證
    • 我們自研的那些Devops工具

    轉(zhuǎn)載于:https://www.cnblogs.com/37Y37/p/10514575.html

    總結(jié)

    以上是生活随笔為你收集整理的Django内置权限扩展案例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。