權限管理---設計分析以及具體細節

設計分析以及具體細節

提及權限咱們你們都知道,不同的角色會有不同的權限。好比就像學生管理系統同樣,管理員,老師,學生之間的權限都是不同的,那麼展現的頁面也是不同的。因此,咱們如今來看看具體操做。html

目標:生成一個獨立的組件,到哪都能用python

1、先建立一個 項目,建一個app01和rbac的應用

2、表結構設計

一、先看配置文件合適不,給建立的rbac在配置文件裏面設置一下
    找到INSTALLED_APPS=['rbac']數據庫

 

 

 

 

 

 

 

 

配置靜態文件

二、設計表結構

    models中建立類:五個類,七張表
    角色表:
    用戶表:
    權限表:django

    權限組表:session

    菜單表: app

 

角色表和權限表是多對多的關係(一個角色能夠有多個權限,一個權限能夠對應多個角色)
用戶表和角色表是多對多的關係(一個用戶能夠有多個角色,一個角色有多個用戶)函數

因此有會多生成兩張關係表url

    一個菜單下面有多個組spa

    一個組下面有多個菜單設計

    一個菜單下面有多個權限

 

from django.db import models

# Create your models here.
class Role(models.Model):
    title = models.CharField(max_length=32,verbose_name="角色")
    permissions = models.ManyToManyField(to="Permission",verbose_name="擁有權限的角色",blank=True)  #權限和角色是多對多的關係

    def __str__(self):
        return self.title
    class Meta:
        verbose_name_plural = "角色表"

class Permission(models.Model):
    title = models.CharField(max_length=32,verbose_name="權限名")
    url = models.CharField(max_length=32,verbose_name="帶正則的url")
    codes = models.CharField(max_length=32,verbose_name="代碼")
    group = models.ForeignKey(to="Group",verbose_name="所屬組",blank=True)  #組和權限是一對多的關係,一個組有多個權限
    menu_gp = models.ForeignKey(to='Permission',related_name='aaa',null=True,blank=True,verbose_name="組內菜單")
    def __str__(self):
        return self.title
    class Meta:
        verbose_name_plural = "權限表"

class UserInfo(models.Model):
    name = models.CharField(max_length=32,verbose_name="姓名")
    password = models.CharField(max_length=64,verbose_name="密碼")
    email = models.CharField(max_length=32,verbose_name="郵箱")
    roles = models.ManyToManyField(to="Role",blank=True)  #用戶和角色是多對多的關係
    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural = "用戶表"

class Group(models.Model):
    title = models.CharField(max_length=32,verbose_name="組名稱")
    menu = models.ForeignKey(to="Menu",verbose_name="組內菜單",blank=True)  #一個組下有多個菜單
    def __str__(self):
        return self.title
    class Meta:
        verbose_name_plural = "權限組"

class Menu(models.Model):
    caption = models.CharField(max_length=32,verbose_name="菜單")
    def __str__(self):
        return self.caption
    class Meta:
        verbose_name_plural = "菜單表"

 

具體分析爲何要多加個code列和權限組表呢?

一、咱們通常是先看到的是列表頁面,在這個頁面上是否顯示添加,是否顯示編輯,是否顯示刪除,都是須要判斷的
   有無添加權限,有無刪除權限,有無編輯權限,咱們能夠給每個url一個代號

dict = {
    1:{                    代號
          /userinfo/            list
       /userinfo/add/       add
       /userinfo/del(\d+)/    del 
       /userinfo/edit(\d+)/    edit
    }
  }

不只在列表頁面須要知道他有那些權限,在其餘頁面也知道他有那些權限
因此上面的方案仍是有點很差,那麼咱們採起下面的方案。將代號取出來放在一個列表裏面

dict = {
      1:{
              "codes":["list","add","del","edit"]
             urls:[
                "/userinfo/",
                "/userinfo/add"/,
                "/userinfo/del(\d+)/ ",
                "/userinfo/edit(\d+)/ ",
              ]    
        }
      2:{
           "codes":{"list","add","del","edit"}
            urls:[
                 "/order",
                 "/order/add"/,
                  "/order/del(\d+)/ ",
                 "/order/edit(\d+)/ ",
               ]    
       }
}            

把這個字典存到session中當你訪問頁面的時候我就知道你有什麼權限一個url對應一個code 多個url對應一個組

注意:
  關聯字段 null = True        數據庫用的時候能夠爲空
  關聯字段 blank = True     admin用的時候能夠爲空

當出現這個錯誤的時候

 

 

 解決辦法

python manage.py migrate --fake 廢棄

 

3、經過django-admin錄入權限數據

- 先建立一個超級用戶 python3 manage.py createsuperuser
    - 用戶名 root
    - 密碼 zhy123456
    - 在admin.py 中
        from rbac import models
        admin.site.register(models.Permission)
        admin.site.register(models.Role)
        admin.site.register(models.UserInfo)
      這樣的話上去的是英文的,若是你想讓中文顯示就在類中加一個類
        class Meta:
           verbose_name_plural = "權限表"
      - 當你給關聯字段錄入數據的時候會有錯誤提示,那麼在類中你的那個關聯字段在加一個屬性blank = True 能夠爲空
      permissions = models.ManyToManyField(to="Permission",verbose_name="具備的全部權限", blank=True)

4、編寫登陸

1.編寫登陸
2.若是用戶驗證成功就設置session
3.先查出當前用戶的全部的權限
4.從這些權限中找到全部的url,吧這些url放到session中
   這些都是在rbac裏面的操做,若是咱們作一些複雜的操做,可能會有好多的代碼
 咱們寫rbac的目的是作成一個公共的組件,爲了讓別人省事
 咱們在建立一個server的文件夾,裏面建立一個init_permission的py文件。

 

結構化數據:方便之後作操做。。。

dict = {
      1:{
              "codes":["list","add","del","edit"]
             urls:[
                "/userinfo/",
                "/userinfo/add"/,
                "/userinfo/del(\d+)/ ",
                "/userinfo/edit(\d+)/ ",
              ]    
        }
      2:{
           "codes":{"list","add","del","edit"}
            urls:[
                 "/order",
                 "/order/add"/,
                  "/order/del(\d+)/ ",
                 "/order/edit(\d+)/ ",
               ]    
       }
}            

5.拿到用戶請求的url去session裏面作驗證
  獲取當前請求的url
  獲取session中保存當前用戶的權限
  而後開始驗證
  若是匹配成功就有權訪問
  若是匹配不成功就無權訪問
  用re去匹配的時候,re.match(/userinfo/,/userinfo/add) #都能匹配到
  那麼要記得在匹配正則的時候加個起始符和終止符regex = "^{0}$".format(url)
  def login(request):
    .....
    設置session
  def index(request):
    ....
    獲取session
  def userinfo(request):
    獲取session
這樣若是有好多個函數,就的重複好多代碼,咱們能夠用中間件來處理

中間件和裝飾器的區別:
  中間件用來作批量處理
  若是函數很少的話能夠用加裝飾器的方法 

5、中間件:獲取session,而且當用戶匹配成功的時候,先把code保存在request中,方便之後判斷 

一、記得要配置白名單 

 

 

 

 

 

 

 

二、必須繼承MiddlewareMixin這個類

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response

 

6、設計權限管理-----問題:在訪問列表頁面時,是否須要判斷有無添加權限、有無刪除權限、有無編輯權限。 

views

def userinfo(request):
    # 方式一
    # Page_permission = request.permission_code_list
    # 方式二:實例化
    page_permission = BasePagePermission(request.permission_code_list)
    print("page_permission",request.permission_code_list)
    data_list = [
        {"id":1,"name":"xxx1"},
        {"id":2,"name":"xxx2"},
        {"id":3,"name":"xxx3"},
        {"id":4,"name":"xxx4"},
        {"id":5,"name":"xxx5"},
    ]
    return render(request,"userinfo.html",{"data_list":data_list,"page_permission":page_permission})

 

在模板userinfo.html中:兩種使用方式

方式一:

<table>
    {% if "add" in Page_permission %}
        <a href="#">添加</a>
    {% endif %}
    {% for row in data_list %}
         <tr>
            <td>{{ row.id }}</td>
            <td>{{ row.name }}</td>
             {% if "edit" in Page_permission %}
                <td><a href="#">編輯</a></td>
             {% endif %}
           {% if "del" in Page_permission %}
                 <td>{<a href="#">刪除</a></td>
           {% endif %}
        </tr>
    {% endfor %}
</table>

若是不想像上面同樣每一個都判斷,那麼還有第二種方法,

方式二:

吧permission_code_list處理一下
在views中定義一個類
class BasePagePermission(object):
    def __init__(self,code_list):
        self.code_list = code_list
    def has_add(self):
        if "add" in self.code_list:
            return True
    def has_del(self):
        if "del" in self.code_list:
            return True
    def has_edit(self):
        if "edit" in self.code_list:
            return True
實例化:page_permission = BasePagePermission(request.permission_code_list)
在模板中
<table>
    {% if page_permission.has_add %}
        <a href="#">添加</a>
    {% endif %}
    {% for row in data_list %}
         <tr>
            <td>{{ row.id }}</td>
            <td>{{ row.name }}</td>
             {% if page_permission.has_edit %}
                <td><a href="#">編輯</a></td>
             {% endif %}
           {% if page_permission.has_del %}
                 <td>{<a href="#">刪除</a></td>
           {% endif %}
        </tr>
    {% endfor %}
</table>

7、設計菜單管理-----問題:

一、如何生成菜單

二、怎麼讓這些菜單分級顯示而且若是當前訪問的url權限默認展開若是是組內菜單就加粗或者變紅

三、非菜單url,默認選中原菜單。(若是你是點擊用戶列表進來的,那麼你看到頁面了,若是你點擊添加的時候,你的那個用戶列看不見了,這就很差了。因此要設計當你點擊添加按鈕的時候,那個用戶列表被默認選中)

菜單管理
  菜單一
    用戶管理
    權限管理
  菜單二
    訂單管理
    角色管理

分級作了菜單。這些菜單該顯示什麼菜單?是當前用戶登陸以後從數據庫拿到這個用戶擁有的權限,而後把權限搞成菜單

在表裏面設計了一個組內菜單(自關聯 ),當menu_gp_id爲NULL就表明能夠做爲菜單

 

 

 

 

 

 

 

 

一、在初始化的時候,初始化權限信息,獲取權限信息並放置到session中

menu_list = []
    for item in permission_list:
        tpl = {
            "id":item["permissions__id"],
            "title":item["permissions__title"],
            "url":item["permissions__url"],
            "menu_gp_id":item["permissions__menu_gp_id"],
            "menu_id":item["permissions__group__menu_id"],
            "menu_title":item["permissions__group__menu__caption"]
        }
        menu_list.append(tpl)
    request.session[settings.PERMISSION_MENU_KEY] = menu_list

重要: 

由於是要在頁面上渲染,通常咱們會在視圖函數的render裏面加{"":變量}這樣渲染,
可是還有個更好用的方法:用自定義的標籤

具體操做:
  一、找到app建立一個templatetags的文件夾
  二、而後在裏面隨便建立一個文件
  三、導入form django.template import Library
       register = Library()

      方式一:
        @register.simple_tag
        def menu():
          return "菜單"     這裏返回啥頁面上就顯示啥
        而後在母版裏面導入mnue.html
        {% load rbac %}

      方式二:
        @register.includsion_tag("xxx.html") #這裏存放的是html文件,,,@register.includsion_tag("xxx.html")   自動會讀這個文件而且把返回值拿到在頁面上渲染
        def menu():
          return "菜單"     這裏返回啥頁面上就顯示啥
        「在母版中:{%menu_html  request%}     request是參數,記得要加上{% load rbac %}

 四、注意:
        若是有兩個文件夾同名,避免發生衝突:就再建立一個文件夾包起來

二、去Session中獲取菜單相關信息,匹配當前URL,生成菜單

先把和菜單相關的全部字段取出來

menu_list = [
    {'id': 1, 'title': '用戶列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜單二'}, 
    {'id': 2, 'title': '添加用戶', 'url': '/userinfo/add/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜單二'}, 
    {'id': 3, 'title': '刪除用戶', 'url': '/userinfo/del/(\\d+)/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜單二'}, 
    {'id': 4, 'title': '編輯用戶', 'url': '/userinfo/edit/(\\d+)/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜單二'}, 
    {'id': 5, 'title': '訂單列表','url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜單一'}, 
    {'id': 6, 'title': '添加訂單', 'url': '/order/add/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜單一'}, 
    {'id': 7, 'title': '刪除訂單', 'url': '/order/del/(\\d+)/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜單一'}, 
    {'id': 8, 'title': '編輯訂單', 'url': '/order/edit/(\\d+)/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜單一'}
]

而後循環列表找出能夠做爲菜單的權限

{
    1: {'id': 1, 'title': '用戶列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜單二'}, 
    5: {'id': 5, 'title': '訂單列表', 'url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜單一'}
}

再次循環列表向上邊的字典中添加active

{
    1: {'id': 1, 'title': '用戶列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜單二', 'active': True},
    5: {'id': 5, 'title': '訂單列表', 'url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜單一'}
}

結構化數據(吧上面獲得的數據化成下面這樣格式的,方便之後使用)

{
    1: {
            'menu_id': 1,
            'menu_title': '菜單一',
            'active': None, 
            'children': [
                    {'title': '訂單列表', 'url': '/order/', 'active': None}
                ]
            }
    2: {
        'menu_id': 2, 
        'menu_title': '菜單二', 
        'active': True,
        'children': [
                {'title': '用戶列表', 'url': '/userinfo/', 'active': True}
            ]
        },
    
}
相關文章
相關標籤/搜索