權限管理

數據庫設計分析

舉個例子,一個初創公司中(CEO,產品總監,技術攻城獅,搬磚的)...寥寥幾人,每一個人可能會同時扮演多種角色(每種角色相對應都有必定不一樣的權限)html

那麼,人 角色 權限三者間存在一種怎樣的聯繫(又該怎樣生成數據庫表)數據庫

So,能夠肯定了  用戶表、角色表、權限表session

  • 用戶與角色是多對多的關係。
  • URL表(權限表)對應的權限的行爲(功能表)是多對多的關係。
  • 如今角色能夠分配相應的功能了,它們也是多對多的關係。
  • 那麼還有一件事,權限應該掛載在菜單下面,因此菜單表和權限表又是一種ForeignKey的關係
class Userinfo(models.Model):
    """
    用戶表
    """
    nickname = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

    def __str__(self):
        return self.nickname


class Role(models.Model):
    """
    角色表
    """
    caption = models.CharField(max_length=32)

    def __str__(self):
        return self.caption


class UserinfoToRole(models.Model):
    """
    用戶能夠扮演多種角色
    角色也能夠對應多個用戶
    """
    u = models.ForeignKey('Userinfo')
    r = models.ForeignKey('Role')

    def __str__(self):
        return '%s-%s' %(self.u.nickname, self.r.caption)


class Url(models.Model):
    """
    權限Url表  和菜單有外鍵關係  應該掛載在相應的菜單下面
    """
    caption = models.CharField(max_length=32)
    url = models.CharField(max_length=32)
    menu = models.ForeignKey('Menu', null=True, blank=True)

    def __str__(self):
        return '%s-%s' %(self.caption, self.url)


class Action(models.Model):
    """
    功能權限表 例如  一、增 二、刪 三、改 四、查
    """
    caption = models.CharField(max_length=32)
    code = models.CharField(max_length=32)

    def __str__(self):
        return self.code


class UrlToAction(models.Model):
    """
    權限分配小功能
    一個URL可能會有增 刪功能,另外一個可能全有
    """
    url = models.ForeignKey('Url')
    a = models.ForeignKey('Action')

    def __str__(self):
        return '%s-%s' %(self.url.caption, self.a.caption)


class RoleToUrlToAction(models.Model):
    """
    角色分配權限表,功能能夠對應多種角色,角色也能夠對應多種功能
    """
    uTa = models.ForeignKey('UrlToAction')
    r = models.ForeignKey('Role')

    def __str__(self):
        return '%s-%s' %(self.r.caption, self.uTa)


class Menu(models.Model):
    """
    菜單表
    """
    caption = models.CharField(max_length=32)
    m = models.ForeignKey('self', related_name='mTom', null=True, blank=True)

    def __str__(self):
        return self.caption
權限表生成DEMO

根據當前登錄用戶獲取所對應角色的權限及相應權限行爲(功能)系列

用戶登錄成功以後,經一系列驗證以後......app

能夠經過用戶名去獲取當前登錄用戶扮演了哪些角色,這些角色下面又有哪些權限下的全部功能???數據庫設計

經過上面的表關係,,,大概能夠經過四種方式能夠獲取當前用戶所扮演的角色ide

    login_username = request.POST.get('user')
    # 根據登錄用戶獲取 用戶扮演的角色
    login_user = models.Userinfo.objects.filter(nickname=login_username).first()
    # 方式一
    # role_list = models.UserinfoToRole.objects.filter(u=login_user)
    # 方式二
    # role_list = models.Role.objects.filter(userinfotorole__u=login_user)
    # 方式三
    role_list = models.Role.objects.filter(userinfotorole__u__nickname=login_username)
    # 方式四
    # 若是有多對多第三字段,經過多對多字段取
    # 前提:m = models.ManyToManyField("Role")
    # user_obj = models.User.objects.get(username=username)
    # role_list = user_obj.m.all() 
current_loginuser角色扮演

上面代碼中,獲取所扮演的角色列表role_list,接下來應該這麼些角色相應的都有哪些功能(各種權限url下的功能)優化

    # 獲取角色下全部的權限
    # 我的全部權限都將保存在session中,往後做匹配使用且沒法實時更新,需從新登錄生效
    # 方式一
    # roleTourlToaction_list = models.RoleToUrlToAction.objects.filter(r__in=role_list)
    # 方式二
    # 不一樣角色可能相對應一樣的功能,故而去重
    roleTourlToaction_list = models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).\
        values('url__url', 'a__code').distinct()
角色扮演下全部功能獲取

如今能夠公開的情報:url

  • 獲取我的的全部權限列表,放置在session當中。能夠以後在對用戶Url(權限)訪問進行比較。缺點:沒法獲取實時權限信息,需從新登錄
  • 獲取到全部功能後,能夠經過Url去重的方式獲取用戶權限(Url)
  • 且應該在菜單中顯示的權限
menu_leaf_list = models.UrlToAction.objects.\
filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True).\
        values('url__id', 'url__url', 'url__caption', 'url__menu').distinct()
獲取菜單下的權限(葉子)

接下來應該構建一些東西了,而且很是巧妙......spa

A、構建權限(葉子節點)字典設計

    menu_leaf_dict = {}
    for item in menu_leaf_list:
        item = {
            'id': item['url__id'],
            'url': item['url__url'],
            'caption': item['url__caption'],
            'parent_id': item['url__menu'],
            'child': []
        }
        if item['parent_id'] in menu_leaf_dict:
            menu_leaf_dict[item['parent_id']].append(item)
        else:
            menu_leaf_dict[item['parent_id']] = [item, ]
        import re
        if re.match(item['url'], request.path):
            item['open'] = True
            open_leaf_parent_id = item['parent_id']
    # 此步構建了權限字典(字典的鍵爲菜單的ID,即權限掛載在哪一個菜單下)
    # 且用正則驗證當前用戶訪問url和權限url進行匹配, 返回成功即爲打開狀態
    # print(menu_leaf_dict)
巧妙之處(一)

B、構建全部菜單字典

    # 獲取全部的菜單列表(每條數據爲一個字典)
    menu_list = models.Menu.objects.values('id', 'caption', 'm__id')
    menu_dict = {}
    for item in menu_list:
        item['child'] = []            # 爲每一個菜單設置一個孩子列表
        item['status'] = False        # 是否顯示
        item['open'] = False          # 是否打開
        menu_dict[item['id']] = item  # 菜單字典賦值操做
    # 此步構建了菜單字典(鍵爲每條菜單的id, 值爲每條菜單數據並附加了一些內容)
巧妙之處(二)

C、將Url(權限)掛載在與之對應菜單字典上(找父親啊找父親),生成全新的菜單字典

    for k, v in menu_leaf_dict.items():
        menu_dict[k]['child'] = v
        parent_id = k
        # 將後代中有葉子節點的菜單標記爲【顯示】
        while parent_id:
            menu_dict[parent_id]['status'] = True
            parent_id = menu_dict[parent_id]['parent_id']
    # 將已經選中的菜單標記爲【展開】
    while open_leaf_parent_id:
        menu_dict[open_leaf_parent_id]['open'] = True
        open_leaf_parent_id = menu_dict[open_leaf_parent_id]['parent_id']
    # 此步將權限(url)掛載到了菜單的最後一層
    # 而且將權限的全部直接父級的status改成了True !妙哉
    # 再且若用戶當前訪問Url與權限(url)匹配,open則爲打開狀態 !妙哉妙哉
    # 返回了全新的菜單字典
    # print(menu_dict)
巧妙之處(三)

D、處理等級關係,場景應用:層級評論...

    result = []
    for row in menu_dict.values():
        if not row['parent_id']:
            # 表示爲根級菜單
            result.append(row)
        else:
            # 子級菜單相應的去父菜單的child下面
            menu_dict[row['parent_id']]['child'].append(row)
    print(result)
    # 此步將全部的層級關係作了處理,造成簡潔明瞭的樹形結構
巧妙之處(四)

E、頁面HTML顯示菜單層級顯示(遞歸實現)

    response = ''
    tpl = """
        <div class="item {0}">
            <div class="title">{1}</div>
            <div class="content">{2}</div>
        </div>
    """
    for row in result:
        # 若是狀態爲False,則不顯示
        if not row['status']:
            continue
        active = ''
        if row['open']:
            print('ok')
            active = 'active'
        title = row['caption']
        content = menu_cotent(row['child'])
        response += tpl.format(active, title, content)

    return render(request, 'index.html', {'response': response})


def menu_cotent(child_list):
    """
    遞歸生成html
    :param child_list: 子級列表
    :return:
    """
    response = ''
    tpl = """
        <div class="item {0}">
            <div class="title">{1}</div>
            <div class="content">{2}</div>
        </div>
    """
    for row in child_list:
        if not row['status']:  # status 
            continue
        active = ''
        if row['open']:  # open_leaf_parent_id
            active = 'active'
        if 'url' in row:
            # 若是url存在於row中, 則表示到了最終權限節點
            response += """<a href="%s" class="%s">%s</a>""" \
                        %(row['url'], active, row['caption'])
        else:
            title = row['caption']
            content = menu_cotent(row['child'])
            response += tpl.format(active, title, content)
    return response
巧妙之處(五)

F、優化(類之整理)

上述代碼貌似看起來很繁瑣,So!下面代碼將上文中數據構建及生成多級菜單封裝到了類裏面

class MenuHelper(object):
    def __init__(self, request, username):
        # 當前請求的request對象
        self.request = request
        # 當前登錄的用戶
        self.username = username
        # 當前訪問Url
        self.current_url = request.path
        # 獲取當前用戶的全部權限
        self.permission2action_dict = None
        # 獲取在菜單中顯示的權限
        self.menu_leaf_list = None
        # 獲取全部菜單
        self.menu_list = None

        self.session_data()

    def session_data(self):
        # 獲取用戶的全部權限信息, 做用於用戶訪問
        permission_dict = self.request.session.get('permission_info')
        if permission_dict:
            self.permission2action_dict = permission_dict['permission2action_dict']
            self.menu_leaf_list = permission_dict['menu_leaf_list']
            self.menu_list = permission_dict['menu_list']
        else:
            # 獲取當前登錄用戶全部角色
            role_list = models.Role.objects.filter(userinfotorole__u=self.username)
            # 獲取角色的全部行爲列表
            roleTourlToaction_list = models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).\
                values('url__url', 'a__code').distinct()
            # 構建行爲字典
            roleTourlToaction_dict = {}
            for item in roleTourlToaction_list:
                if item['url__url'] in roleTourlToaction_dict:
                    roleTourlToaction_dict[item['url__url']].append(item['a__code'])
                else:
                    roleTourlToaction_dict[item['url__url']] = [item['a__code'], ]

            # 獲取菜單的葉子節點, 即顯示在菜單的最後一層
            menu_leaf_list = models.UrlToAction.objects. \
                filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True). \
                values('url__id', 'url__url', 'url__caption', 'url__menu').distinct()
            # 獲取全部的菜單列表
            menu_list = models.Menu.objects.values('id', 'caption', 'parent_id')

            self.request.session['permission_info'] = {
                'permission2action_dict': roleTourlToaction_dict,
                'menu_leaf_list': menu_leaf_list,
                'menu_list': menu_list
            }

            self.permission2action_dict = roleTourlToaction_dict
            self.menu_leaf_list = menu_leaf_list
            self.menu_list = menu_list

    def menu_data(self):
        menu_leaf_dict = {}
        open_leaf_parent_id = None

        # 歸併全部的葉子節點
        for item in self.menu_leaf_list:
            item = {
                'id': item['url__id'],
                'url': item['url__url'],
                'caption': item['url__caption'],
                'parent_id': item['url__menu'],
                'child': [],
                'status': False,
                'open': False
            }
            if item['parent_id'] in menu_leaf_dict:
                menu_leaf_dict[item['parent_id']].append(item)
            else:
                menu_leaf_dict[item['parent_id']] = [item, ]
            import re
            if re.match(item['url'], self.current_url):
                item['open'] = True
                open_leaf_parent_id = item['parent_id']

        # 生成菜單字典
        menu_dict = {}
        for item in self.menu_list:
            item['child'] = []
            item['status'] = False
            item['open'] = False
            menu_dict[item['id']] = item
        # 將葉子節點添加到菜單字典中...
        for k, v in menu_leaf_dict.items():
            menu_dict[k]['child'] = v
            parent_id = k
            # 將後代中有葉子節點的菜單標記爲【顯示】
            while parent_id:
                menu_dict[parent_id]['status'] = True
                parent_id = menu_dict[parent_id]['parent_id']
        # 將已經選中的菜單標記爲【展開】
        while open_leaf_parent_id:
            menu_dict[open_leaf_parent_id]['open'] = True
            open_leaf_parent_id = menu_dict[open_leaf_parent_id]['parent_id']
        # 生成樹形結構數據
        result = []
        for row in menu_dict.values():
            if not row['parent_id']:
                result.append(row)
            else:
                menu_dict[row['parent_id']]['child'].append(row)
        return result

    def menu_tree(self):
        response = ''
        tpl = """
            <div class="item {0}">
                <div class="title">{1}</div>
                <div class="content">{2}</div>
            </div>
        """
        result = self.menu_data()
        for row in result:
            if not row['status']:
                continue
            active = ''
            if row['open']:
                print('ok')
                active = 'active'
            title = row['caption']
            content = self.menu_cotent(row['child'])
            response += tpl.format(active, title, content)
        return response

    def menu_cotent(self, child_list):
        response = ''
        tpl = """
                <div class="item {0}">
                    <div class="title">{1}</div>
                    <div class="content">{2}</div>
                </div>
            """
        for row in child_list:
            if not row['status']:  # status
                continue
            active = ''
            if row['open']:  # open_leaf_parent_id
                active = 'active'
            if 'url' in row:
                # 若是url存在於row中, 則表示到了最終權限節點
                response += """<a href="%s" class="%s">%s</a>""" \
                            % (row['url'], active, row['caption'])
            else:
                title = row['caption']
                content = self.menu_cotent(row['child'])
                response += tpl.format(active, title, content)
        return response
類的封裝

權限管理簡單應用

Views

class MenuHelper(object):
    def __init__(self, request, username, current_url):
        # 當前請求的request對象
        self.request = request
        # 當前登錄的用戶
        self.username = username
        # 當前訪問Url
        self.current_url = current_url
        # 獲取當前用戶的全部權限
        self.permission2action_dict = None
        # 獲取在菜單中顯示的權限
        self.menu_leaf_list = None
        # 獲取全部菜單
        self.menu_list = None

        self.session_data()

    def session_data(self):
        # 獲取用戶的全部權限信息, 做用於用戶訪問
        permission_dict = self.request.session.get('permission_info')
        if permission_dict:
            self.permission2action_dict = permission_dict['permission2action_dict']
            self.menu_leaf_list = permission_dict['menu_leaf_list']
            self.menu_list = permission_dict['menu_list']
        else:
            # 獲取當前登錄用戶全部角色
            role_list = models.Role.objects.filter(userinfotorole__u__nickname=self.username)
            # 獲取角色的全部行爲列表
            roleTourlToaction_list = list(models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).\
                values('url__url', 'a__code').distinct())
            # 構建行爲字典
            roleTourlToaction_dict = {}
            for item in roleTourlToaction_list:
                if item['url__url'] in roleTourlToaction_dict:
                    roleTourlToaction_dict[item['url__url']].append(item['a__code'])
                else:
                    roleTourlToaction_dict[item['url__url']] = [item['a__code'], ]

            # 獲取菜單的葉子節點, 即顯示在菜單的最後一層
            menu_leaf_list = list(models.UrlToAction.objects. \
                filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True). \
                values('url__id', 'url__url', 'url__caption', 'url__menu').distinct())
            # 獲取全部的菜單列表
            menu_list = list(models.Menu.objects.values('id', 'caption', 'parent_id'))

            self.request.session['permission_info'] = {
                'permission2action_dict': roleTourlToaction_dict,
                'menu_leaf_list': menu_leaf_list,
                'menu_list': menu_list
            }

            self.permission2action_dict = roleTourlToaction_dict
            self.menu_leaf_list = menu_leaf_list
            self.menu_list = menu_list

    def menu_data(self):
        menu_leaf_dict = {}
        open_leaf_parent_id = None

        # 歸併全部的葉子節點
        for item in self.menu_leaf_list:
            item = {
                'id': item['url__id'],
                'url': item['url__url'],
                'caption': item['url__caption'],
                'parent_id': item['url__menu'],
                'child': [],
                'status': True,
                'open': False
            }
            if item['parent_id'] in menu_leaf_dict:
                menu_leaf_dict[item['parent_id']].append(item)
            else:
                menu_leaf_dict[item['parent_id']] = [item, ]
            import re
            if re.match(item['url'], self.current_url):
                item['open'] = True
                open_leaf_parent_id = item['parent_id']

        # 生成菜單字典
        menu_dict = {}
        for item in self.menu_list:
            item['child'] = []
            item['status'] = False
            item['open'] = False
            menu_dict[item['id']] = item
        # 將葉子節點添加到菜單字典中...
        for k, v in menu_leaf_dict.items():
            menu_dict[k]['child'] = v
            parent_id = k
            # 將後代中有葉子節點的菜單標記爲【顯示】
            while parent_id:
                menu_dict[parent_id]['status'] = True
                parent_id = menu_dict[parent_id]['parent_id']
        print(menu_dict)
        # 將已經選中的菜單標記爲【展開】
        while open_leaf_parent_id:
            menu_dict[open_leaf_parent_id]['open'] = True
            open_leaf_parent_id = menu_dict[open_leaf_parent_id]['parent_id']
        # 生成樹形結構數據
        result = []
        for row in menu_dict.values():
            if not row['parent_id']:
                result.append(row)
            else:
                menu_dict[row['parent_id']]['child'].append(row)
        return result

    def menu_tree(self):
        response = ''
        tpl = """
            <div class="item {0}">
                <div class="title">{1}</div>
                <div class="content">{2}</div>
            </div>
        """
        result = self.menu_data()
        for row in result:
            if not row['status']:
                continue
            active = ''
            if row['open']:
                active = 'active'
            title = row['caption']
            content = self.menu_cotent(row['child'])
            response += tpl.format(active, title, content)
        return response

    def menu_cotent(self, child_list):
        response = ''
        tpl = """
                <div class="item {0}">
                    <div class="title">{1}</div>
                    <div class="content">{2}</div>
                </div>
            """
        for row in child_list:
            if not row['status']:  # status
                continue
            active = ''
            if row['open']:  # open_leaf_parent_id
                active = 'active'
            if 'url' in row:
                # 若是url存在於row中, 則表示到了最終權限節點
                response += """<a href="%s" class="%s">%s</a>""" \
                            % (row['url'], active, row['caption'])
            else:
                title = row['caption']
                content = self.menu_cotent(row['child'])
                response += tpl.format(active, title, content)
        return response


def login(request):
    user_request_url = '/girl.html'
    login_user = request.GET.get('user')
    obj = MenuHelper(request, login_user, user_request_url)
    string = obj.menu_tree()
    return render(request, 'index.html',{'menu_string': string})
Views

Html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .content{
            margin-left: 20px;
            display: none;
        }
        .content a{
            display: block;
        }
        .active>.content{
            display: block;
        }
    </style>
</head>
<body>
    {{ menu_string | safe }}
</body>
</html>
Html

經過更改URL(至關於用戶訪問的權限url)從而看到顯示的菜單權限

權限管理實際應用

更新中...

相關文章
相關標籤/搜索