Django的Rbac的介紹2
上一篇部落格我們記錄了一下Django中使用Rbac,但是上一篇部落格中的方法有一點不好,就是,因為我要在html檔案中控制:如果使用者有某個許可權,則顯示這個許可權所代表的按鈕,但是我現在只有1張表的增刪改查,但是如果我有多張表呢,我難道要每張表都寫一次類似下面的程式碼嗎?
{% if "/user/del/(\d+)" in per_list %} <a href="/user/del/{{ user.id }}"><button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> 刪除</button></a> {% endif %}
這樣就必須麻煩了,因為上面是user表,如果我還有一張role的表,我是不是也要在寫一遍呢?
{% if "/roles/del/(\d+)" in per_list %} <a href="/user/del/{{ user.id }}"><button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> 刪除</button></a> {% endif %}
可能有的同學會說:我當然要每張表寫一次了,但是大家是否可以想一想,不論是roles表還是user表,或者是其他的表,他們的許可權是不是都是一樣的,永遠都逃不出增刪改查4個許可權,那麼我們就可以從這裡入手來進行優化
針對上面的問題,我們可以下面的方式解決
1、首先我們需要重新設計資料庫
使用者表如下,這張表我們沒有做任何改動,和方法1是一樣的
class Userinfo(models.Model): username = models.CharField(max_length=64) uerpwd = models.CharField(max_length=64) roles = models.ManyToManyField(to="Role") def __str__(self): return self.username class Meta: verbose_name = "使用者表" verbose_name_plural = verbose_name
角色表如下,這張表我們沒有做任何改動,和方法1是一樣的
class Role(models.Model): title = models.CharField(max_length=64) pers = models.ManyToManyField(to="per") def __str__(self): return self.title class Meta: verbose_name="角色表" verbose_name_plural = verbose_name
下面的表,我們就和方法1不一樣的了
首先我們看下許可權表
class per(models.Model): title = models.CharField(max_length=64) url = models.CharField(max_length=128) action = models.CharField(max_length=32, default="") group = models.ForeignKey("PerGroup", default=1) def __str__(self): return self.title class Meta: verbose_name = "許可權表" verbose_name_plural = verbose_name
許可權表我們增加了2個欄位
action欄位描述的就是四種操作,增刪改查,比如list就是查,del就刪,edit就是改,add就是增加
group欄位描述的就是我這個許可權屬於哪張表,比如屬於使用者表,或者許可權表
所有我們這裡還需要增加一張表
class PerGroup(models.Model): title = models.CharField(max_length=32) def __str__(self): return self.title class Meta: verbose_name = "許可權組" verbose_name_plural = verbose_name
2、下面我們看下在登陸的檢視函式中是如何處理許可權的,到底把哪些資訊放到session中了
# 2、方案2,是我們重新設計資料庫後的方法 obj = userobj.roles.all().values("pers__url","pers__group_id","pers__action") print(obj) # print(dir(request.session)) # print("=" * 120) # return redirect("/user/") # 構建一個這樣的資料結構 per_dict = {} for item in obj: gid = item.get("pers__group_id") if gid in per_dict.keys(): per_dict[gid]["urls"].append(item["pers__url"]) per_dict[gid]["action"].append(item["pers__action"]) else: per_dict[gid] = { "urls":[item["pers__url"],], "action" :[item["pers__action"],] } request.session["per_dict"] = per_dict return HttpResponse("登陸成功") else: return render(request, "rbac_login.html")
我們實際構建了一個這樣的字典,然後把這個字典放到session中
{
“group_id1”:{
action:[aa,bb,cc],
urls:[aa,bb,cc],
}
"group_id2":{
action:[aa,bb,cc],
urls:[aa,bb,cc]
}
}
3、然後我們看下中介軟體函式是如何處理的
per_dict = request.session.get("per_dict") print(per_dict) for item in per_dict.values(): urls = item["urls"] for reg in urls: reg = "^" + reg + "$" ret = re.match(reg,current_path) if ret: print("action",item["action"]) # 為request物件賦值一個新的變數 request.actions = item["action"] return None return HttpResponse("無許可權訪問")
這裡還有一個知識點,我們為request這個物件賦值了一個變數,那麼以後所有request這個物件都可以去獲取action這個變數的值
其實這裡和方法1是沒有區別的,都是從session中取出urls的資訊,然後和當前的url進行對比,如果能匹配上,則通過。如果匹配不上,則返回無許可權
4、重點是在這裡,主要是在html中處理就會簡單一些
我們先看下檢視函式是如何渲染html頁面的
def user(request): userobj = rbacmodels.Userinfo.objects.all() # 方案1的程式碼處理邏輯 # per_list = request.session.get("per_list") # print(per_list) # return render(request,"rbac_user.html",{"user_list":userobj,"per_list":per_list}) # 方案2的程式碼處理邏輯 uid = request.session.get("userid") # user = rbacmodels.Userinfo.objects.filter(id=uid).first() action = request.actions user_list = rbacmodels.Userinfo.objects.all() return render(request,"rbac_user.html",{"action":action,"user_list":user_list})
首先從request物件中獲取我們在中介軟體函式中賦值的actions變數,然後渲染到前端
我們在看下前端的html頁面,我們只需要判斷這次的使用者對某張表是否有del、list、edit、add許可權就可以了,因為這些資訊我們都單獨放在action中了
{# 方式1#} {% if "/user/del/(\d+)" in per_list %} <a href="/user/del/{{ user.id }}"><button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> 刪除</button></a> {% endif %} {# 方式2#} {% if "del" in request.actions %} <a href="/user/del/{{ user.id }}"><button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> 刪除</button></a> {% endif %}
這裡我們可以對比一下方式1和方式2的區別
只需要判斷是否有增刪改查的許可權就可以了,無需在判斷表的名稱
至此Djang的許可權控制我們就做完了
----------------------------------------------------------------------------------------------------------------------------
我們還有一個點可以優化一下,我們可以重新寫一個類,這個類我們需要傳遞一個action進來,然後定義增刪改查4個方法,每個方法都判斷action中是否有對應的方法就可以了
class Per(object): def __init__(self,actions): self.actions = actions def add(self): return "add" in self.actions def edit(self): return "edit" in self.actions def dele(self): return "del" in self.actions def list(self): return "list" in self.actions
所以我們在前端判斷就更加簡單了
先在檢視函式中例項化一個物件
Per_obj = Per(request.actions) return render(request,"rbac_user.html",{"Per_obj":Per_obj})
然後在htmlz中呼叫這個例項變數的方法就可以了
是不是這樣寫就更加簡單了
好了Django的Rbac我們就介紹到這裡了,謝謝大家關注!