权限 RBAC

权限

一丶RBAC

基于角色权限管理

# 五张表
# 角色表
   # 权限表
   # 用户表
   # 权限与角色表 (权限组表)
   # 角色与用户表   (用户组表)

 

1.权限

2.为什么要有权限?

给不同的用户分配上不同的权限(不同功能)

3.开发上一个权限的组件,为什么要开发的组件?

4.在web开发中,什么是权限?

url代表的是权限

5.表结构的设计

权限表

id url

1 /user_list/

2 /customer_list/

 

用户表

id username pwd

1 root 123

2 alex 123

 

用户和权限的关系表

id user_id permission_id

1 1 1

2 1 2

登陆后保存权限信息

查询

permissions = user_obj.roles.filter(permissions__url__isnull=False).values('permissions__url').distinct()

去空 去重

 

 

内容回顾

权限的控制是如何实现的?

url代表的是权限

表结构的设计

5张表 3个model

权限表

url 权限 url地址正则表达式的字符串 /login/

title 标题

 

角色表

name 角色的名称

permissions 多对多 管理权限

 

用户表

username 用户名

password 密码

roles 多对多 管理角色

 

角色和权限的关系表

用户和角色的关系表

流程:

1.登陆

中间件

  • 白名单

  • 登陆状态的校验

  • 免认证地址的校验

  • 权限的校验

    • 从session中获取权限的信息

    • 正则匹配

      • 匹配成功直接return

  • 拒绝请求

登陆的视图

  • 验证用户名和密码

  • 验证成功 获取到用户对象

  • 根据用户对象查询到权限的信息

    • orm values __跨表 去空 去重 queryset

  • 保存权限信息和登陆状态到session中

    • json的序列化

    • 转成列表

  • 回复重定向 去 index

     

今日安排

1.动态生成一级菜单

2.动态生成二级菜单

销售功能 —— 一级菜单

  • 客户列表 —— 二级菜单

  • 跟进记录

班主任功能

  • 班级管理

     

客户管理

  • 客户列表

财务管理

  • 缴费列表

3.git

git 版本控制

git init 初始化仓库

git add .

git commit -m '描述 备注'

git config --global user.name

git config --global user.email

git status 查看状态

git push origin master

git remote add origin https://gitee.com/maple-shaw/day75.git

git clone https://gitee.com/maple-shaw/day75.git

git pull origin master

 

git reset --hard 版本号 版本回退

git log 查看版本

git reflog 查看历史记录

 

分支

master

git branch 查看分支

git branch 分支 创建分支

git branch -d 分支 删除分支

git checkout 分支 切换分支

git merge dev 将dev分支上的代码合并当当前分支

 

合并分支时会出现冲突 出现冲突手动修改

 

个人开发的流程

创建代码仓库

有两个分支 master dev

在dev分支上 开发了一个功能 提交一个commit

dev分支上的代码合并到master

 

修改BUG的流程:

从master分支上新建debug分支

在debug分支修改bug 提交版本

切换回master 合并debug上分支

删除debug分支

 

 

公司和家配合开发

公司的电脑上创建仓库 写代码 提交版本

推送代码到远程仓库

回家在家里的电脑 克隆仓库

开发新功能 提交 推送到远程仓库

上班在公司

从远程仓库 拉取代码

 

 

  1. 权限组件 RBAC

  2. 如何实现权限控制?

    web应用中 url 代表的权限

    表结构

    简单的权限控制

    权限表

    • url 权限 url的地址 正则表达式 ^$

    • title 标题 分权限看功能

    角色表

    name 角色名称

    permissions 多对多 管理权限

    用户表

    username 用户

    password 密码

    roles 多对多 关联角色

    角色和权限的关系表

    用户和角色的关系表

    动态生成一级菜单

    权限表

    • url 权限 url的地址 正则表达式 ^$

    • title 标题 分权限看功能

    • is_menu 是否是菜单

    • icon 图标

    动态生成二级菜单

    菜单表

    • title 一级菜单的标题

    • icon 图标

    权限表

    • url 权限 url的地址 正则表达式 ^$

    • title 标题 分权限看功能

    • menu 外键 关联菜单表 可为空 menu_id 有menu_id 当前的权限是二级菜单 没有menu_id 普通权限

    对一级菜单进行排序

    菜单表

    • title 一级菜单的标题

    • icon 图标

    • weight 权重 权重大在前面

    非菜单权限归属

    权限表

    • url 权限 url的地址 正则表达式 ^$

    • title 标题 分权限看功能

    • menu 外键 关联菜单表 可为空 menu_id 有menu_id 当前的权限是二级菜单 没有menu_id 普通权限

    • parent 外键 自关联 可为空 parent_id 有parent_id 当前的权限是子权限 没有parent_id 父权限

  3. 流程 + 技术点

    1. 中间件

      1. 方法 process_request

      2. request.current_menu_id = None

      3. request.breadcrumb_list = [ { url :'indedx' 'title':'首页' } ]

      4. 白名单

        1. re

        2. settings

      5. 登陆状态的校验

      6. 免认证的校验

      7. 权限的校验

        1. 从session中获取权限的字典

        2. 循环做校验

          1. re

          2. 匹配成功

            id pid

            判断是否有pid

            - 没有pid  
            当前访问的是二级菜单    
            request.current_menu_id=  id        request.breadcrumb_list.append({url  title })
            - 有pid    
             当前访问的是子权限  
             通过pid找到父权限的信息 p_permission = permissions[str(pid)]
             request.current_menu_id =pid    
             request.breadcrumb_list.append({ url :p_permission['url'],'title':   })
            request.breadcrumb_list.append({ url,title   })
      8. 所有的都匹配不成功 拒绝请求

    2. 登陆的视图

      1. 验证用户名和密码 校验成功 获取用户对象

      2. 初始化权限(菜单)信息

        1. 通过用户对象查询权限

          1. permissions= user_obj.roles.filter(permission__url__isnull=False).values().distinct()

          2. 跨表 去空 去重

        2. 构建数据结构

          简单的权限控制

          权限的列表 list(permissions) [ {'permissions__url':'xxxxx'} ]

          动态生成一级菜单

          权限的列表 [ {'url':'xxxxx'} ]

          菜单的列表 [ {'title' , 'url' 'icon '} ]

          动态生成二级菜单

          权限的列表 [ {'url':'xxxxx'} ]

          菜单的字典

          {

          一级菜单的id:{

          title 一级菜单的标题

          icon 图标

          children: 【

          { url title } 二级菜单的信息

          }

          }

          对一级菜单进行排序

          权限的列表 [ {'url':'xxxxx'} ]

          菜单的字典

          {

          一级菜单的id:{

          title 一级菜单的标题

          icon 图标

          weight 权限

          children: 【

          { url title } 二级菜单的信息

          }

          }

          非菜单权限的归属

          权限的列表 [ {'url':'xxxxx' id pid } ]

          菜单的字典

          {

          一级菜单的id:{

          title 一级菜单的标题

          icon 图标

          weight 权限

          children: 【

          { url title id } 二级菜单的信息

          }

          }

          路径导航

          权限的字典 { id : {'url':'xxxxx' id title pid } }

          菜单的字典

          {

          一级菜单的id:{

          title 一级菜单的标题

          icon 图标

          weight 权限

          children: 【

          { url title id } 二级菜单的信息

          }

          }

        3. 保存权限菜单信息到session中

        4. 重定向到index

      3. 模板

        自定义的方法 inclusion_tag

        menu (需要模板 menu.html css js)

        有序字典

        二级菜单的判断

        breadcrumb

     

  4.  

 

 

 

 

RBAC权限控制

表结构(4个model 6张表)

菜单表

  • title 标题

  • icon 图标

  • weight 权重

 

权限表

  • url 权限 url正则表达式 没有^$

  • title 标题 显示名字

  • name url的别名 (权限控制到按钮级别)

  • menu 外键 关联菜单表 (有menu_id 当前的权限是二级菜单,没有menu_id 是普通权限)

  • parent 外键 自关联 (非菜单权限的归属)

    is_menu

    icon

 

角色表

  • name 名称

  • permissions 多对多 关联权限表

 

用户表

  • username

  • password

  • roles 多对多 关联角色表

角色和权限的关系表

用户和角色的关系表

流程和技术点

  1. 中间件

    • 获取当前访问的url地址

    • request.current_menu_id = None

    • request.breadcrumb_list = [ { url :'index' title:'首页' } ]

    • 白名单

    • 登陆状态的校验

    • 免认证的校验

    • 权限的校验

      • 从session中获取权限的字典

      • 循环权限的字典

        • 正则匹配

        • 匹配成功

          • id pid pname

          • 没有pid 当前访问的是二级菜单 父权限

            • request.current_menu_id =id

            • request.breadcrumb_list .append({ url title }) 当前权限的信息

          • 有pid 当前访问的是子权限

            • request.current_menu_id =pid

            • 获取父权限的信息 permissions[pname]

            • request.breadcrumb_list .append({ url title }) 父权限的信息

            • request.breadcrumb_list .append({ url title }) 当前权限的信息

          • return

    • 拒绝请求

  2. 登陆的视图

    1. 校验用户

    2. 根据用户查询权限相关信息

      • orm 跨表 values 去空 去重

    3. 构建权限和菜单的数据结构

      权限 permission_dict = { name : { url id pid title pname } }

      菜单 menu_dict = {

      一级菜单的id : {

      title icon weight

      children: [

      { url title id }

      ]

      }

      }

    4. 保存数据到session中

    5. 重定向index

  3. 模板

    • 母板和继承

    • 动态生成二级菜单

      • inclusion_tag

      • 有序字典

      • sorted

      • 循环二级菜单

        • 比较 当前二级菜单的id request.current_menu_id

        • 比较成功 给一级菜单的 class=‘hidden’ 移除掉 给当前的二级菜单加上class=‘active’

      • 模板中两次for循环

    • 路径导航

      • inclusion_tag

      • 循环 request.breadcrumb_list

    • 权限控制到按钮级别

      • filer - has_permission

      • {{ request|has_permission:name }}

        • name in request.session['permission'] 返回true

1.批量操作

新增权限 路由系统中有 数据库中没有 路由系统name的集合 - 数据库name的集合

待更新的权限 路由系统中有 数据库中也有 路由系统name的集合 & 数据库name的集合

删除的权限 路由系统没有 数据库中有 数据库name的集合 - 路由系统name的集合

2.分配权限

3.权限组件的应用

  1. 拷贝rbac的app 到项目中并且注册

    INSTALLED_APPS = [
    ...
      'rbac.apps.RbacConfig',
    ]
  2. 数据库的迁移

    1. 修改用户

      class User(models.Model):
         # username = models.CharField('用户名', max_length=32)
         # password = models.CharField('密码', max_length=32)
         roles = models.ManyToManyField(Role, verbose_name='用户所拥有的角色', blank=True)

         def __str__(self):
             return self.username

         class Meta:
             abstract = True # 基类 不会在数据库中生成表 让别的表继承

      继承

      from rbac.models import User
      class UserProfile(User):
    2. 执行迁移

      1. 先删除rbac 下migrations中除了__init__.py 之外的py文件

      2. 执行命令

        1. python manage.py makemigrations

        2. python manage.py migrate

  3. 路由配置

    urlpatterns = [
    ...
       url(r'^rbac/', include('rbac.urls', namespace='rbac')),

    ]
  4. 录入权限信息

    角色管理 http://127.0.0.1:8000/rbac/role/list/

    菜单管理 http://127.0.0.1:8000/rbac/menu/list/

    批量添加权限 http://127.0.0.1:8000/rbac/multi/permissions/

    菜单分配二级菜单 以及子权限

  5. 分配权限

    确定用户 如果使用不是rbac的User 需要修改rbac/views.py中的User为当前使用的用户表

    给角色分配权限

    给用户分配角色

  6. 应用权限

    加中间件

    MIDDLEWARE = [
    ....
       'rbac.middlewares.rbac.RbacMiddleWare'
       
    ]

    在settings中配置权限的相关配置

    # rbac 白名单
    WHITE_LIST = [
       r'^/crm/login/$',
       r'^/crm/reg/$',
       r'^/admin/'
    ]

    # 免认证的地址
    PASS_AUTH_LIST = [
       r'^/index/$'
    ]

    # 权限的session的key
    PERMISSION_SESSION_KEY = 'permission'
    # 菜单的session的key
    MENU_SESSION_KEY = 'menus'
  7. 修改登录的视图

    登录成功后初始化权限信息

    from rbac.service.init_permission import init_permission
    # 登录后初始化
    init_permission(obj,request)
  8. 动态生成二级菜单

    在模板中修改:

    {% load  rbac %}
    {% menu request %}

    导入css js

    <link rel="stylesheet" href="{% static 'rbac/css/menu.css' %} "/>
    <script src="{% static 'rbac/js/menu.js' %} "></script>
  9. 路径导航

    {% breadcrumb request %}

10 .权限控制到按钮级别

{% if request|has_permission:'crm:consult_record_add' %}
   <a href="{% url 'crm:consult_record_add' %}" class="btn btn-primary btn-sm">新增</a>
{% endif %}

 

 

crm 业务 + rbac

crm 客户关系管理系统

功能:

  1. 登陆注册

  2. 销售

    1. 客户信息管理

      公户和私户 防止抢单

      公户 所有人都能查看 没有绑定销售

      私户 只有他的销售能看 绑定销售

    2. 跟进记录的管理

    3. 报名表的管理

    4. 缴费记录管理

  3. 班主任

    1. 班级的管理

    2. 课程记录的管理

    3. 学习记录的管理

展示信息:

  1. 普通字典

    对象.字段名

  2. choices

    对象.字段名 数据库的结果

    对象.get_字段名_display()

  3. 外键

    对象.外键.name __str__

  4. 自定义方法

    model中定义方法

新增和编辑

设计两个url地址 + 一个视图(modelform ) + 一个模板

删除

crm的表:

用户 部门 校区 客户 跟进记录 报名 缴费 班级表 课程记录 学习记录表

权限 : 菜单 权限 角色 用户 角色和权限 用户和角色

权限的控制:

  1. url 代表权限

  2. 表结构的设计

  3. 流程

    1. 登陆成功后获取用户的权限 保存到session中

      获取权限

      user_obj.roles.filter('permission__url__isnull=False).values('permission__url').distinct()

    2. 中间件实现权限的校验

      1. 白名单的校验

      2. 登陆状态的校验

      3. 免认证地址的校验

      4. 权限的校验

      5. 拒绝请求

表结构 
# 实现简单的权限控制
class Permission(models.Model):
   url = models.CharField(max_length=200, verbose_name='权限')
   title = models.CharField(max_length=32, verbose_name='标题')


class Role(models.Model):
   name = models.CharField(max_length=32, verbose_name='角色名称')
   permission = models.ManyToManyField(Permission, verbose_name='角色所拥有的权限')


class User(models.Model):
   name = models.CharField(max_length=32, verbose_name='用户名')
   password = models.CharField(max_length=32, verbose_name='密码')
   roles = models.ManyToManyField(Role, verbose_name='用户所拥有的角色')

 

permission_lit = [ { 'permission__url':'xxxx' } ]

   
# 一级菜单
class Permission(models.Model):
   url = models.CharField(max_length=200, verbose_name='权限')
   title = models.CharField(max_length=32, verbose_name='标题')
   is_menu = models.BooleanField(default=False)
   icon =  models.CharField(max_length=100, verbose_name='图标')


class Role(models.Model):
   name = models.CharField(max_length=32, verbose_name='角色名称')
   permission = models.ManyToManyField(Permission, verbose_name='角色所拥有的权限')


class User(models.Model):
   name = models.CharField(max_length=32, verbose_name='用户名')
   password = models.CharField(max_length=32, verbose_name='密码')
   roles = models.ManyToManyField(Role, verbose_name='用户所拥有的角色')

   
permission_lit = [ { 'url':'xxxx' } ]
menu_lit = [ { 'url':'xxxx' ,title: ,icon } ]


# 二级菜单
class Menu(models.Model):
   title = models.CharField(max_length=32, verbose_name='菜单名')
   icon = models.CharField(max_length=100, verbose_name='图标')


class Permission(models.Model):
   url = models.CharField(max_length=200, verbose_name='权限')
   title = models.CharField(max_length=32, verbose_name='标题')
   menu = models.ForeignKey(Menu, on_delete=models.CASCADE, null=True, blank=True)


class Role(models.Model):
   name = models.CharField(max_length=32, verbose_name='角色名称')
   permission = models.ManyToManyField(Permission, verbose_name='角色所拥有的权限')


class User(models.Model):
   name = models.CharField(max_length=32, verbose_name='用户名')
   password = models.CharField(max_length=32, verbose_name='密码')
   roles = models.ManyToManyField(Role, verbose_name='用户所拥有的角色')

 
permission_lit = [ { 'url':'xxxx' } ]
menu_dict = {
一级菜单的id : {
      title :
      icon :
      children: [
              { url  title }
              ]
  }
}


# 一级菜单排序
class Menu(models.Model):
   title = models.CharField(max_length=32, verbose_name='菜单名')
   icon = models.CharField(max_length=100, verbose_name='图标')
   weight = models.IntegerField(default=1)


class Permission(models.Model):
   url = models.CharField(max_length=200, verbose_name='权限')
   title = models.CharField(max_length=32, verbose_name='标题')
   menu = models.ForeignKey(Menu, on_delete=models.CASCADE, null=True, blank=True)


class Role(models.Model):
   name = models.CharField(max_length=32, verbose_name='角色名称')
   permission = models.ManyToManyField(Permission, verbose_name='角色所拥有的权限')


class User(models.Model):
   name = models.CharField(max_length=32, verbose_name='用户名')
   password = models.CharField(max_length=32, verbose_name='密码')
   roles = models.ManyToManyField(Role, verbose_name='用户所拥有的角色')

permission_lit = [ { 'url':'xxxx' } ]
menu_dict = {
一级菜单的id : {
      title :
      icon :
      weight: 10
      children: [
              { url  title }
              ]
  }
}

# 非菜单权限归属
class Menu(models.Model):
   title = models.CharField(max_length=32, verbose_name='菜单名')
   icon = models.CharField(max_length=100, verbose_name='图标')
   weight = models.IntegerField(default=1)


class Permission(models.Model):
   url = models.CharField(max_length=200, verbose_name='权限')
   title = models.CharField(max_length=32, verbose_name='标题')
   menu = models.ForeignKey(Menu, on_delete=models.CASCADE, null=True, blank=True)
   parent = models.ForeignKey('self', null=True, blank=True)


class Role(models.Model):
   name = models.CharField(max_length=32, verbose_name='角色名称')
   permission = models.ManyToManyField(Permission, verbose_name='角色所拥有的权限')


class User(models.Model):
   name = models.CharField(max_length=32, verbose_name='用户名')
   password = models.CharField(max_length=32, verbose_name='密码')
   roles = models.ManyToManyField(Role, verbose_name='用户所拥有的角色')


permission_lit = [ { 'url':'xxxx' ,'id','pid' } ]
menu_dict = {
一级菜单的id : {
      title :
      icon :
      weight: 10
      children: [
              { url  title  id   }
              ]
  }
}

# 权限校验时
获取权限的pid
  没有pid   当前访问的是二级菜单    request.current_menu_id = id
       有pid     当前访问的是子权限      request.current_menu_id = pid
       
       
# 路径导航

permission_dict = {  id :{ 'url':'xxxx' ,'id','pid'  'title' }}
menu_dict = {
一级菜单的id : {
      title :
      icon :
      weight: 10
      children: [
              { url  title  id   }
              ]
  }
}

# 权限校验时
request.breadcrumb_list = [ {'title'  首页  'url' 地址 } ]
获取权限的pid
  没有pid   当前访问的是二级菜单  
      request.current_menu_id = id
           request.breadcrumb_list.append({'title'   'url'})
       有pid     当前访问的是子权限      request.current_menu_id = pid
      request.breadcrumb_list.append({'title'   'url'})  # 根据pid找到父权限
      request.breadcrumb_list.append({'title'   'url'})

# 权限控制到按钮级别
if 判断    filter  
# 权限控制到按钮级别
class Menu(models.Model):
   title = models.CharField(max_length=32, verbose_name='菜单名')
   icon = models.CharField(max_length=100, verbose_name='图标')
   weight = models.IntegerField(default=1)


class Permission(models.Model):
   url = models.CharField(max_length=200, verbose_name='权限')
   title = models.CharField(max_length=32, verbose_name='标题')
   name = models.CharField(max_length=32, verbose_name='url别名')
   menu = models.ForeignKey(Menu, on_delete=models.CASCADE, null=True, blank=True)
   parent = models.ForeignKey('self', null=True, blank=True)


class Role(models.Model):
   name = models.CharField(max_length=32, verbose_name='角色名称')
   permission = models.ManyToManyField(Permission, verbose_name='角色所拥有的权限')


class User(models.Model):
   name = models.CharField(max_length=32, verbose_name='用户名')
   password = models.CharField(max_length=32, verbose_name='密码')
   roles = models.ManyToManyField(Role, verbose_name='用户所拥有的角色')

permission_dict = { name :{ 'url':'xxxx' ,'id', 'pid'  'title' 'pname' }}
menu_dict = {
一级菜单的id : {
      title :
      icon :
      weight: 10
      children: [
              { url  title  id   }
              ]
  }
}

不需要重新登陆,就更新权限?

1. 用户表记录session_key 
2. 权限变化时找到session数据,更新权限就可以


# 操作session  
from django.contrib.sessions.models import Session  # model
解密
request.session.decode('xxxxx')
加密
request.session.encode({})
# 查询自身的字典
request.session.load()

如何实现把权限粒度控制到数据行?

 

posted on 2020-03-02 12:26  向往1  阅读(153)  评论(0编辑  收藏  举报

导航

……