楚阿旗

博客园 首页 新随笔 联系 订阅 管理

一個網站如果沒有權限控管,就等於是沒有保安的辦公大樓,所有人都可以任意進出任何地點使用任何資料。

但我們也希望能夠製作一個可以插拔的權限控管,可以先把主要業務邏輯做好以後,再把權限控管加入,不用做太大的更動。

 

目前這個組件的目錄結構是長這樣子:

rbac
├─service
│      ├─middlewares.py
│      └─rbac.py
├─template
│ └─menu.html ├─tempaletags │ └─myilters.py └─models.py

 

我學習的是rbac(Role Based Access Control)權限控制模型,也就是依照使用者角色來區分權限。

這個方式的好處就是,不必在同一個權限上重複添加使用者,而是藉由角色來套用權限,減少儲存空間的浪費。

 

這是ㄧ個基本的表結構,可以讓我們建立一個一級選單:

from django.db import models

#用戶表
class User(models.Model):
    name = models.CharField(max_length=32)
    # password = models.CharField(max_length=32)
    roles = models.ManyToManyField("Role")

    def __str__(self):
        return self.name

#角色表
class Role(models.Model):
    title = models.CharField(max_length=32)
    permissions = models.ManyToManyField("Permission")
    def __str__(self):
        return self.title

#權限表
class Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)
    is_menu =models.BooleanField(default=False)
    icon = models.CharField(max_length=32)

    def __str__(self):
        return self.title

首先創建表結構,表結構包含用戶、角色、權限,還有另外兩個關係表,這兩個關係表關係如下:

一個使用者可以對應到多個角色,這些角色又有其對應權限。

 User<---------多對多--------->Role

Role<---------多對多--------->Permission 

 

在用戶登入以後,我們將該用戶所有的權限,寫到session中的權限列表中,這樣子我們未來要判斷用戶權限,只要透過session,就可以取得該用戶的所擁有的權限。

service/rbac.py

from rbac import models


def initial_session(name,request):
    """
    功能:將當前登入人的所有權限錄入session中
    """
    # 查出當前用戶的所有權限列表,使用distnct()將所有重複項除去
    permissions = models.User.objects.filter(name=name).values("roles__permissions__url",
                                                               "roles__permissions__is_menu",
                                                               "roles__permissions__title",
                                                               "roles__permissions__icon"
                                                               ).distinct()
    permission_list = []
    permission_menu_list = []
    for item in permissions:
        #將用戶權限寫入權限列表
        permission_list.append(item["roles__permissions__url"])
        #選單權限列表
        #在寫入權限時,一併將選單權限寫入
        if item["roles__permissions__is_menu"]:
            permission_menu_list.append({
                'title':item["roles__permissions__title"],
                'icon':item["roles__permissions__icon"],
                'url':item["roles__permissions__url"]
            })
    # 將當前登錄人的權限列表寫入session中
    request.session["permission_list"] = permission_list
    # 將當前登錄人的左側選單寫入session中
    request.session["permission_menu_list"] = permission_menu_list
    print('permission_menu_list',permission_menu_list)

 

因為必須讓權限套用在全局,我們採用中間件的方式,這樣子就不用對每個視圖單獨做設置。

service/middleware.py中寫入以下程式碼:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,redirect
import re

class PermissionMiddleWare(MiddlewareMixin):
    def process_request(self,request):
        #獲取當前路徑
        current_path = request.path
        #設置白名單
        for reg in ["/admin*","/crm/login/","/crm/validimg/","/crm/logout/"]:
            if re.search(reg,current_path):
                return None
        #校驗用戶是否已登入
        user_id = request.session.get("user_id")
        if not user_id:
            return redirect("/crm/login/")

        #校驗權限
        #獲取session中寫入的用戶權限列表
        #如果用戶訪問的頁面不在權限列表中,禁止該用戶訪問    
        permission_list = request.session.get("permission_list")
        for reg in permission_list:
            reg = "^%s$" % reg
            ret = re.search(reg,current_path)
            if ret:
                return None
        return HttpResponse("無權訪問")

選單會動態生成,所以我們將選單獨立為一個menu.html文件

{% load myfilters %}
{% for item in permission_menu_list %}
<li class="nav-item">
    <a href="{{ item.url }}" class="nav-link {{ item.active }}">
      <i class="{{ item.icon }} nav-icon"></i>
      <p>{{item.title}}</p>
    </a>
</li>
{% endfor %}

選單有被選中或未選中的差異,在class中加入"active"來區隔。判斷上面就是當 request.path == reg_path 時,將active加入

from django.template import Library
from django.utils.safestring import mark_safe
import re

register = Library()

#自定義一個includsion標籤
@register.inclusion_tag("rbac/menu.html")
def get_menu_style(request):
    permission_menu_list = request.session['permission_menu_list']
    for item in permission_menu_list:
         # 使用re來判斷當前url是否在選單權限列表中
         if re.search(item['url'],request.path):
              # 如果找到的話,在該字典中加入一個active元素,用來渲染頁面
              item['active'] = 'active'
    return {'permission_menu_list':permission_menu_list}

 

二級選單:

更多時候選單會摺疊起來

 

 這時候就會需要二級選單,這邊就不能再使用一級選單的列表資料結構,列表無法呈現多個層級的選單結構,所以要使用字典來表現。

使用字典會如同下方的格式:

permission_menu_list:
    {
      1:{
        "title":"層級一",
        "icon":"",
        "children":[
            {
              "title":"層級二",
              "url":"",
            },
            {
               "title":"層級二",
               "url":"",
            },
        ]
      },
      2:{
           "title":"層級一",
           "icon":"",
           "children":[
            {
              "title":"層級二",
              "url":"",
            },
            {
               "title":"層級二",
               "url":"",
            },
        ]
       },
    }

未完....

 

 

posted on 2019-11-25 10:50  楚阿旗  阅读(116)  评论(0)    收藏  举报