CMDB 权限系统

CMDB权限系统

权限系统

业务场景: CMDB --

用户名密码
    生成环境的机器:
        root        运维
        user        运维
        readonly    运维/开发/测试/项目主管

    灰度环境的机器:
        root        运维
        user        运维/测试
        readonly    运维/开发/测试/项目主管

    测试环境的机器:
        root        运维
        user        运维/测试
       readonly    运维/开发/测试/项目主管



主机
    生成环境的机器: (CRUD/增删改查)
        运维         CRUD
        开发         R
        测试         R
        项目主管     CR

    灰度环境的机器:
        运维         CRUD
        开发         R
        测试         R
        项目主管     CR

    测试环境的机器:
        运维         CRUD
        开发         R
        测试         R
        项目主管     CR



分析:
  权限:
  人:  -> 组(岗位)-> 按钮(url+路径)-> 路径是重点 /list.html -> 组(url组) -> 菜单

  路径: list.html  -> 查看类 R /host/list.html  /user/list.html  /readolny/list.html
  路径: add.html   -> 增加类 C
  路径: delete.html  -> 删除类 D
  路径: update.html  -> 编辑类 U

图解:

 
 
 

表设计

简单的 5张 表

class UserInfo(models.Model):    # 登录用户表
    name = models.CharField(max_length=32, blank=True, null=True, verbose_name='登录用户名')
    passwd = models.CharField(max_length=128, blank=True, null=True, verbose_name='登录密码')
    department = models.CharField(max_length=32, blank=True, null=True, verbose_name='所属部门')

    # ForeignKey   # 一个岗位 对 多个用户 一对多, foreignkey 的 放在 对多的表里    # 用 related_name 反向查找方便
    pos = models.ForeignKey(to='Position', blank=True, null=True, verbose_name='权限', related_name='userpos')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '主机来源表'


class Position(models.Model):    # 岗位,职位 表
    name = models.CharField(max_length=32,blank=True, null=True, verbose_name='职位名')

    # ManyToMany  # 多个职位  对应 多个权限                          # 用 related_name 反向查找方便
    auth = models.ManyToManyField(to='Auth', blank=True, null=True, verbose_name='权限', related_name='posauth')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '职位表'



class Auth(models.Model):    # 权限表
    url = models.CharField(max_length=128, blank=True, null=True, verbose_name='路径')
    name = models.CharField(max_length=64, blank=True, null=True, verbose_name='显示标识')

    # ForeignKey   # 一个组 对应多个 权限                     # 用 related_name 反向查找方便
    group = models.ForeignKey(to='AuthGroup', blank=True, null=True, verbose_name='组', related_name='authgroup')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '权限表'



class AuthGroup(models.Model):     # 组表
    name = models.CharField(max_length=16, blank=True, null=True, verbose_name='组名')

    # ForeignKey    # 一个组 对应 多个菜单
    ti = models.ForeignKey(to='Menu', blank=True, null=True, verbose_name='菜单', related_name='groupmenu')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '组表'



class Menu(models.Model):     # 菜单表
    title = models.CharField(max_length=32, blank=True, null=True, verbose_name='菜单名')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = '菜单栏'

创建数据库,并迁移
python manage.py makemigrations

python manage.py migrate

修改 admin.py 将其显示在后台界面

from django.contrib import admin

# Register your models here.
from hc.models import UserInfo
from hc.models import Position
from hc.models import Auth
from hc.models import AuthGroup
from hc.models import Menu

admin.site.register(UserInfo)
admin.site.register(Position)
admin.site.register(Auth)
admin.site.register(AuthGroup)
admin.site.register(Menu)

图解

权限表设计
 
 

登录django admin后台进行
添加数据
添加表关系

url

/user/list/
/user/add/
/user/update/(\d+)/
/user/delete/(\d+)/

/menu/list/
/menu/add/
/menu/update/(\d+)/
/menu/delete/(\d+)/

/position/list/
/position/add/
/position/update/(\d+)/
/position/delete/(\d+)/


/auth/list/
/auth/add/
/auth/update/(\d+)/
/auth/delete/(\d+)/


/authgroup/list/
/authgroup/add/
/authgroup/update/(\d+)/
/authgroup/delete/(\d+)/


/host/list/
/host/add/
/host/update/(\d+)/
/host/delete/(\d+)/

登录后数据需求分析

通过ORM 取得其数据

列如:
老板 /auth/list/ 查询权限 权限表 权限
老板 /auth/add/ 添加权限 权限表 权限
老板 /auth/update/(\d+)/ 编辑权限 权限表 权限
老板 /auth/delete/(\d+)/ 删除权限 权限表 权限
老板 /host/list/ 查询主机 主机 主机
老板 /host/add/ 添加主机 主机 主机
老板 /host/update/(\d+)/ 编辑主机 主机 主机
老板 /host/delete/(\d+)/ 删除主机 主机 主机

数据的应用场景:

    url : 
        1 . --> 菜单的跳转地址 : /host/list/
        2 . --> 验证用户有没有改地址的访问权限   -> 按钮 -> 删除、编辑
   
    {权限:   #template -> 循环这个字典取到值,  <a href="{{xx.pos__auth__name}}"> {{xx.pos__auth__name}}</a>  #通过<a>标签跳转
        [{'pos__auth__name':'查询权限' , 'pos__auth__url':'/auth/list/' } ,  # /auth/list/ -> 按钮 -> 删除、编辑 都放在查看的页面上,
        {'pos__name': 查询岗位},
        {'pos__auth__group__ti__title': 查询菜单},
        {'name': 查询登录用户},
        {'pos__auth__group__name': 查询权限组},
      ]}

为了解决 跳转页面时,也保留菜单栏的数据,
可以再auth表里添加一个 foreignkey 关联自己的方式自锁

to_display = models.ForeignKey(to='Auth', blank=True, null=True, verbose_name='显示', related_name='authauth')
{ 'pos__auth__group__ti__title': [        #  这里仅查询    # 对应url
       { 'pos__auth__name':'查询权限' ,  'pos__auth__url': '/auth/list/' },
       {  },
       {  },
       {  },
       ]
  }

判断登录用户的权限

权限
        职位表(组)    # 有就循环,没有就过
                 查看*   ->  根据 权限,输出 URL      # 岗位 对应 增查改删的权限
                                     boss -> 增查改删
                                     运维  -> 增查改
                                     测试  -> 增查
                                     开发  -> 查
        菜单表(组)     # 有就循环,没有就过
                 查看*   ->  根据 权限,输出 URL 
                                     boss -> 增查改删
                                     运维  -> 增查改
                                     测试  -> 增查
                                     开发  -> 查
        权限表(组)      # 有就循环,没有就过
                 查看*   ->  根据 权限,输出 URL 
                                     boss -> 增查改删
                                     运维  -> 增查改
                                     测试  -> 增查
                                     开发  -> 查
        权限组表(组)      # 有就循环,没有就过
                 查看*   ->  根据 权限,输出 URL 
                                     boss -> 增查改删
                                     运维  -> 增查改
                                     测试  -> 增查
                                     开发  -> 查

主机
        用户表(组)      # 有就循环,没有就过
                 查看*   ->  根据 权限,输出 URL 
                                     boss -> 增查改删
                                     运维  -> 增查改
                                     测试  -> 增查
                                     开发  -> 查
        主机表(组)      # 有就循环,没有就过
                 查看*   ->  根据 权限,输出 URL 
                                     boss -> 增查改删
                                     运维  -> 增查改
                                     测试  -> 增查
                                     开发  -> 查


权限代码演示

登录后要做的两件事:

  1. 把菜单相关的数据拿到
  2. 把用户所有能访问的url拿到

views.py

host_obj = models.UserInfo.objects.filter(name=username, passwd=password)
if host_obj.first():
   obj_all = host_obj.values('name',   # 使用values()方法时,对象必须是queryset_list
                             'pos__name',
                             'pos__auth__url',
                             'pos__auth__name',
                             'pos__auth__group__name',
                             'pos__auth__group__ti__title',
                              )

   menu_dict = {}    # 自定义字典数据
   
   for i in obj_all:
       menu_auth_dict = {
              'url': i.get('pos__auth__url'),   # 赋值
              'name': i.get('pos__auth__name'),
              'display_url': i.get('pos__auth__to_display__url'),
              'display_name': i.get('pos__auth__to_display__name'),               
                              }
       if i.get('pos__auth__group__ti__title') in menu_dict.keys() :
           if not i.get('pos__auth__to_display__name'):
               menu_dict.get(i.get('pos__auth__group__ti__title')).get('lower').append(menu_auth_dict)
       else:
           menu_dict.get(i.get('pos__auth__group__ti__title')) = {}
           menu_dict.get(i.get('pos__auth__group__ti__title')).get('title') = i.get('pos__auth__group__ti__title')
           if not i.get('pos__auth__to_display__name'):
               menu_dict.get(i.get('pos__auth__group__ti__title')).get('lower') = [menu_auth_dict,]
           else:
               menu_dict.get(i.get('pos__auth__group__ti__title')).get('lower') = []
     
   print ('菜单 --- ', menu_dict)

   auth_dict = {}
   for i in obj_all:
       if i.get('pos__auth__group__name') in auth_dict.keys():
           auth_dict.get(i.get('pos__auth__group__name')).get('url').append(i.get('pos__auth__url'))
       else:
           auth_dict.get(i.get('pos__auth__group__name')) = {'url':[i.get('pos__auth__url'),],}

   print ('权限  ---  ',auth_dict)

输出结果:

    菜单
{
    '主机': {
        'title': '主机',
        'lower': [{
            'url': '/user/list/',
            'name': '查询用户',
            'display_url': None,
            'display_name': None
                   }, 
                        {
            'url': '/host/list/',
            'name': '查询主机',
            'display_url': None,
            'display_name': None
            }]
    },
    '权限': {
        'title': '权限',
        'lower': [{
            'url': '/menu/list/',
            'name': '查询菜单',
            'display_url': None,
            'display_name': None
        }, {
            'url': '/position/list/',
            'name': '查询职位',
            'display_url': None,
            'display_name': None
        }, {
            'url': '/authgroup/list/',
            'name': '查询权限组',
            'display_url': None,
            'display_name': None
        }, {
            'url': '/auth/list/',
            'name': '查询权限',
            'display_url': None,
            'display_name': None
        }]
    }
}


    所有权限        --->   django 中间件,判断登录用户的权限,来显示内容
{
    '用户表': {
        'url': ['/user/list/', '/user/add/', '/user/update/(\\d+)/', '/user/delete/(\\d+)/']
    },
    '菜单表': {
        'url': ['/menu/list/', '/menu/add/', '/menu/update/(\\d+)/', '/menu/delete/(\\d+)/']
    },
    '职位表': {
        'url': ['/position/list/', '/position/add/', '/position/update/(\\d+)/', '/position/delete/(\\d+)/']
    },
    '权限组表': {
        'url': ['/authgroup/list/', '/authgroup/add/', '/authgroup/update/(\\d+)/', '/authgroup/delete/(\\d+)/']
    },
    '权限表': {
        'url': ['/auth/list/', '/auth/add/', '/auth/update/(\\d+)/', '/auth/delete/(\\d+)/']
    },
    '主机': {
        'url': ['/host/list/', '/host/add/', '/host/update/(\\d+)/', '/host/delete/(\\d+)/']
    }
}

菜单层级

一级菜单1  -->(1,'')
    第二级菜单(2, 1)
        第三级菜单 (3, 2)

一级菜单2  -->(1,'')
       第二级菜单(2, 1)
           第三级菜单 (3, 2)

# 如果是空的则是母菜单,有值才是该母菜单的子菜单,层级关系

可以封装为一个函数
通过传值即可调用函数

可以将结果存入

  1. 内存
  2. 数据库
  3. NoSQL
  4. session

这里放在session 里方便

views.py 视图

from hc_auth import auth_data    #导入函数

def auth_demo(request):
    if request.method == 'GET':
        return  render(request, 'login_pos.html' ,locals())
    else :
        username = request.POST.get('username')
        password = request.POST.get('password')
        host_obj = models.UserInfo.objects.filter(name=username, passwd=password)
        if host_obj.first():
            auth_data.menu_auth(host_obj, request)     # 调用函数,并传值
            return redirect('/authority/index/')     #
        else:
            return HttpResponse('错误登录')


def index(request):
    menu_dict = request.session.get('menu_dict')
    return render(request, 'auth_index.html', locals())

封装函数体
auth_data.py

def menu_auth(host_obj,request):
    obj_all = host_obj.values('name',  # 使用values()方法时,对象必须是queryset_list
                              'pos__name',
                              'pos__auth__url',  # 这是 url 路径
                              'pos__auth__name',  # 这是 对应的 名字
                              'pos__auth__to_display__url',  # 权限对应权限的 路径
                              'pos__auth__to_display__name',  # 权限对应权限的 名字
                              'pos__auth__group__name',  # 权限组名
                              'pos__auth__group__ti__title',  # 菜单名
                              )
    menu_dict = {}  # 自定义字典数据

    for i in obj_all:
        menu_auth_dict = {      # 临时字典
            'url': i.get('pos__auth__url'),  # 把数据库取到的值,赋给字典
            'name': i.get('pos__auth__name'),
            'display_url': i.get('pos__auth__to_display__url'),  
                   # to_display 里面是对应单个查询的权限名的ID,
            'display_name': i.get('pos__auth__to_display__name'),
                   # ID里面包含了 对应ID 的url + name
        }  # {'url': '/auth/update/(\\d+)/', 'name': '编辑权限', 'display_url': '/auth/list/', 'display_name': '查询权限'}

        # print (i.get('pos__auth__group__ti__title'),menu_dict.keys())  # 菜单名 ( 主机 , dict_keys([]) )
        if i.get('pos__auth__group__ti__title') in menu_dict.keys():  
         # 第一次是空字典,所以是走else
            if not i.get('pos__auth__to_display__name'): 
              # 第二次 字典有值了 判断 to_display 一对多关系
                # not + None 为负负得正
                menu_dict[i.get('pos__auth__group__ti__title')]['lower'].append(menu_auth_dict)
                # 将字典的lower 的值, 是一个列表 [{},{},{}],往里面添加列表的值(字典) -> append(dict)
        else:
            menu_dict[i.get('pos__auth__group__ti__title')] = {}  # 创建新字典格式  # {'主机': {}}
            menu_dict[i.get('pos__auth__group__ti__title')]['title'] = i.get('pos__auth__group__ti__title')
            # print (menu_dict[i.get('pos__auth__group__ti__title')]) # {'title': '主机'}
            # print (menu_dict[i.get('pos__auth__group__ti__title')]['title'] )   # title -> 主机
            # print (i.get('pos__auth__to_display__name'))   # -> None 取不到
            if not i.get('pos__auth__to_display__name'):  # not + None 为负负得正
                menu_dict[i.get('pos__auth__group__ti__title')]['lower'] = [menu_auth_dict, ]
                # 创建 lower  ->   {'lower': [{}]}
            else:
                menu_dict[i.get('pos__auth__group__ti__title')]['lower'] = []
                # i.get('pos__auth__to_display__name') 取到值了,就创建列表

    # print('菜单 --- ', menu_dict)
    request.session['menu_dict']=menu_dict   # 存入session

    auth_dict = {}
    for i in obj_all:
        if i.get('pos__auth__group__name') in auth_dict.keys():
            auth_dict.get(i.get('pos__auth__group__name')).get('url').append(i.get('pos__auth__url'))
            # 将每个url 以列表的append的方法添加进去,成为单个权限组名的 值 {'用户表':{url: ['a','b','c',]}, }
        else:
            auth_dict[i.get('pos__auth__group__name')] = {'url': [i.get('pos__auth__url'), ], }
            # 将 权限组名 作为 auth_dict 字典的 keys, 将对应权限组的url  作为 auth_dict 字典的 values

    # print('权限  ---  ', auth_dict)
    request.session['auth_dict']=auth_dict   # 存入session

templates
auth_index.html

<div id='auth_index'>
   {% for i in menu_dict.values %}
      <h2>{{i.title}}</h2>
      {% for ii in i.lower %}
          <div>
              <a href="{{ii.url}}">{{ii.name}}</a>
          </div>
      {% endfor %}
    {% endfor %}
</div>
 

代码整理及总结

中间件判断
middleware

import re

VALID_LIST = ['/authority/demo/',]   # 白名单 列表

class Middleware_auth(MiddlewareMixin):
    def process_request(self, request):
        current_url = request.path_info
        # print ('当前的url  --->   ',current_url)   # 取 当前的访问的 url
        
        # 白名单设置
        for i in AUTH_LIST:
            if re.match(i, current_url):    # 匹配白名单,匹配不到 就返回 空,不走if里面的内容
                return None    #  匹配 中了 就往 视图 走

        # 取session信息 并判断
        auth_dict = request.session.get('auth_dict')  # 取 当前请求的 session 信息
        # print('session 信息 ---> ',auth_dict)
        if not auth_dict:    # Not + (取不到值就为空,取到了就是true)  # 负负得正走if 的内容,负正跳过当前if 判断
            return redirect('/authority/demo/')    # 跳去权限登录页面

        flag = False   # 打标记开关
        for group_name, auth_url in auth_dict.items():   # 取到的是个字典,用for循环可以吧items的key和values拿出来用
            for url in auth_url.get('url'):    # 因为一个K和V的字典,所用直接get values 才能 来循环里面的url
                # print ('--------- url的拼接 ----------')
                # print(url)    # url
                regax = "^{0}$".format(url)    # 将它拼接
                # print (regax) # 拼接的结果
                if re.match(regax, current_url): # 循环判断数据库里的url。  再用re 模块来判断它是否匹配该当前的url
                    request.permission_code_list = auth_url['url']  #往request里面增加数据,增加的数据是匹配的数据
                    flag = True  # 打开标志,停止当前循环
                    break
            if flag:   # 标志位为 True 联动开关,停止循环
                break

        if not flag:  # 如果上面的匹配没通过,则 标志位不变,即为 无权限用户,跳转至提示页面
            return HttpResponse('该用户无权访问')

str. format 字符串拼接技巧
http://www.runoob.com/python/att-string-format.html

 

template 模板的继承

{% extends 'auth_index.html' %}
    # 继承它需要在 /host/list/  页面的视图里添加数据
    # menu_dict = request.session.get('menu_dict') 
{% block content %}
    {% if '/host/add/' in pos_list %}
        <div>
            <a href="/host/add/"> 添加主机 </a>
        </div>
    {% endif %}
    <table class="table table-bordered table-hover" id="table">
    <h3> 当前主机的详细信息! </h3>
            <thead >
                <tr>
                    <th>    </th>
                    <th>  主机名  </th>
                    <th>  实例名  </th>
                    <th>  CPU  </th>
                    <th>  内存/G  </th>
                    <th>  带宽/M  </th>
                    <th>  登录端口  </th>
                    <th>  公网IP  </th>
                    <th>  主机状态 </th>
                    <th>  内核版本  </th>
                     <th>  操作系统  </th>
                    <th>  来源IP  </th>
                    <th>  标签  </th>
                    <th>地区</th>
                    {% if '/host/update/(\\d+)/' in pos_list %}
                        <th>编辑</th>
                    {% endif %}
                    {% if '/host/delete/(\\d+)/' in pos_list %}
                        <th>删除</th>
                    {% endif %}
                </tr>
            </thead>
            <tbody>
                {% for host in hosts_page %}
                    <tr>
                        <td>{{ host.id }}</td>
                        <td>{{ host }}</td>
                        <td> {{ host.ecsname }} </td>
                        <td> {{ host.cpu }} </td>
                        <td> {{ host.mem }} </td>
                        <td> {{ host.speed }} </td>
                        <td> {{ host.login_port }} </td>
                        <td> {{ host.eth1_network }} </td>
                        <td> {{ host.get_state_display  }} </td>
                        <td> {{ host.kernel }} </td>
                        <td> {{ host.os }} </td>
                        <td> {{ host.source }} </td>

                        <td> {{ host.lab }} </td>
                        <td>{{ host.region }}</td>
                        {% if '/host/update/(\\d+)/' in pos_list %}
                            <td><a href="/host/edit/{{ host.id }}"> 编辑 </a></td>
                              # (\d+) 因为我url里写了匹配数字,所以这里直接可以写成"/host/edit/{{ host.id }}"
                        {% endif %}
                        {% if '/host/delete/(\\d+)/' in pos_list %}
                            <td><a href="/host/del?id={{ host.id }}"> 删除 </a></td>
                        {% endif %}
                    </tr>
                {% endfor %}
            </tbody>
    </table >
 
posted @ 2019-04-23 18:11  H·c  阅读(305)  评论(0编辑  收藏  举报