RABC权限控制(页面操作角色,权限和进行分配)

Posted leixiaobai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RABC权限控制(页面操作角色,权限和进行分配)相关的知识,希望对你有一定的参考价值。

 上一节主要说的是如何通过url进行权限控制,这一节就说一下如何开发一个权限控制的界面,这样我们就能很方便的创建角色,并分配给用户不同角色和不同权限.

1.编写角色管理页面

这个编写较为简单,主要是通过modelform快速实现的,下面代码比较简单,我就不多说了

效果图如下:

技术图片

代码如下:

def role_list(request):
    """角色列表"""
    roles_list = Role.objects.all()
    # 分页
    current_page_num = request.GET.get(page)
    pagination = MyPagination(current_page_num, roles_list.count(), request)
    roles_list = roles_list[pagination.start:pagination.end]
    return render(request, rbac/role_list.html, roles_list: roles_list, pagination:pagination)


def role_operate(request, edit_id=None):
    """角色操作"""
    role_obj = Role.objects.filter(pk=edit_id).first()
    if request.method == "POST":
        role_form = RoleModelForm(request.POST, instance=role_obj)
        if role_form.is_valid():
            role_form.save()
            return redirect(reverse(rbac:role_list))
        return render(request, rbac/role_operate.html, role_form: role_form)
    role_form = RoleModelForm(instance=role_obj)
    return render(request, rbac/role_operate.html, role_form: role_form, role_obj: role_obj)


def role_del(request, del_id):
    """删除角色"""
    Role.objects.filter(pk=del_id).delete()
    return redirect(reverse(rbac:role_list))
RoleModelForm,这里就只有一个name字段,不给角色字段是创建完用户后到时再分配
class RoleModelForm(forms.ModelForm):
    """角色的modelform"""
    class Meta:
        model = Role
        fields = [name]

        error_messages = 
            name: required: 角色名称不能为空
        
        widgets = 
            name: wid.TextInput(attrs=class: form-control)
        

 

 2.编写菜单权限管理页面

这个也不是特别难,因为主要就是两张表在页面渲染的事情,效果图如下:

技术图片

代码如下:

def menu_list(request):
    """菜单权限列表"""
    # 获取所有的菜单
    menu_list = Menu.objects.all()
    # 菜单管理目前选择的菜单名称id
    mid = request.GET.get(mid)
    # 如果mid有值则通过二级菜单中菜单id是一级菜单的和二级菜单下子权限id属于一级菜单的全部找出来显示,没有则显示全部菜单
    if mid:
        permission_list = Permission.objects.filter(Q(parent__menu__id=mid) | Q(menu_id=mid))
    else:
        permission_list = Permission.objects.all()
    # 查询出权限表中的所有字段
    all_permission_list = permission_list.values(id, url, title, url_name,
                                                 menu_id, parent_id, menu__title)
    # 把所有菜单以字典形式保存在字典中
    all_permission_dict = 
    # 第一次for循环将二级菜单加入字典中
    for permission in all_permission_list:
        menu_id = permission.get(menu_id)
        # 有menu_id就证明是二级菜单,加入字典
        if menu_id:
            permission[children] = []
            all_permission_dict[permission[id]] = permission
    # 第二次for循环将三级菜单(子权限)加入到二级菜单的children中
    for permission in all_permission_list:
        parent_id = permission.get(parent_id)
        if parent_id:
            all_permission_dict[parent_id][children].append(permission)
    return render(request, rbac/menu_list.html, menu_list: menu_list,
                                                   all_permission_dict: all_permission_dict, mid: mid)


def menu_operate(request, edit_id=None):
    """菜单管理操作"""
    menu_obj = Menu.objects.filter(pk=edit_id).first()
    if request.method == "POST":
        form_obj = MenuModelForm(request.POST, instance=menu_obj)
        if form_obj.is_valid():
            form_obj.save()
            return redirect(reverse(rbac:menu_list))
        return render(request, rbac/menu_operate.html, form_obj: form_obj)
    form_obj = MenuModelForm(instance=menu_obj)
    return render(request, rbac/menu_operate.html, form_obj: form_obj, menu_obj: menu_obj)


def menu_del(request, del_id):
    """菜单管理删除"""
    Menu.objects.filter(pk=del_id).delete()
    return redirect(reverse(rbac:menu_list))


def permission_operate(request, edit_id=None):
    """权限管理操作"""
    permission_obj = Permission.objects.filter(pk=edit_id).first()
    if request.method == "POST":
        form_obj = PermissionModelForm(request.POST, instance=permission_obj)
        if form_obj.is_valid():
            form_obj.save()
            return redirect(reverse(rbac:menu_list))
        return render(request, rbac/permission_operate.html, form_obj: form_obj)
    form_obj = PermissionModelForm(instance=permission_obj)
    return render(request, rbac/permission_operate.html, form_obj: form_obj, permission_obj: permission_obj)


def permission_del(request, del_id):
    """权限管理删除"""
    Permission.objects.filter(pk=del_id).delete()
    return redirect(reverse(rbac:menu_list))

页面对上面两个数据的for循环展示(这也是最主要的数据展示部分)

% for p_permission in all_permission_dict.values %
                        <tr class="parent" id=" p_permission.id ">
                            <td class="title"><i class="fa fa-caret-down"></i> p_permission.title </td>
                            <td> p_permission.url </td>
                            <td> p_permission.url_name </td>
                            <td></td>
                            <td> p_permission.menu__title </td>
                            <td>
                                <a href="% url ‘rbac:permission_edit‘ p_permission.id %"><i class="fa fa-edit"></i></a>
                                <a style="margin-left: 10px" href="% url ‘rbac:permission_del‘ p_permission.id %"><i
                                        class="fa fa-trash-o text-danger"></i></a>
                            </td>
                        </tr>
                        % for c_permission in p_permission.children %
                            <tr pid=" c_permission.parent_id ">
                                <td style="padding-left: 20px"> c_permission.title </td>
                                <td> c_permission.url </td>
                                <td> c_permission.url_name </td>
                                <td></td>
                                <td></td>
                                <td>
                                    <a href="% url ‘rbac:permission_edit‘ c_permission.id %"><i class="fa fa-edit"></i></a>
                                    <a style="margin-left: 10px" href="% url ‘rbac:permission_del‘ c_permission.id %"><i
                                            class="fa fa-trash-o text-danger"></i></a>
                                </td>
                            </tr>
                        % endfor %

                    % endfor %

点击二级菜单显示和隐藏,这里巧妙的用到了二级菜单的id和子权限的parent_id相等去显示和隐藏,下面是显示隐藏的jquery代码

<script>
        $(‘.permisson-area‘).on(‘click‘, ‘tr .title‘, function () 
            var caret = $(this).find(‘i‘);
            var id = $(this).parent().attr(‘id‘);
            if (caret.hasClass(‘fa-caret-right‘))
                caret.removeClass(‘fa-caret-right‘).addClass(‘fa-caret-down‘);
               $(this).parent().nextAll(‘tr[pid="‘ + id + ‘"]‘).removeClass(‘hide‘);
            else
                caret.removeClass(‘fa-caret-down‘).addClass(‘fa-caret-right‘);
                $(this).parent().nextAll(‘tr[pid=‘ + id + ‘]‘).addClass(‘hide‘);
            
        )
    </script>

 

 3.分配权限管理页面编写(这个比较麻烦,主要是数据结构比较复杂,嵌套太多层,注意这还是二级菜单)

实现的效果图如下:

技术图片

代码如下,备注在代码中写了,详细请看代码:

def distribute_permissions(request):
    """分配权限"""
    # uid是前端提交的用户id,rid是前端提交的角色id
    uid = request.GET.get(uid)
    rid = request.GET.get(rid)

    # 用户添加角色,由于有多个from表单所以给每个from表单一个postType
    if request.method == POST and request.POST.get(postType) == role and uid:
        user = User.objects.filter(id=uid).first()
        if not user:
            return HttpResponse(用户不存在)
        # 因为是多对多的关系,所以用set就可以直接更新数据了,记得set里面必须是可迭代对象,所以getlist
        user.roles.set(request.POST.getlist(roles))
    # 角色添加权限
    if request.method == POST and request.POST.get(postType) == permission and rid:
        role = Role.objects.filter(id=rid).first()
        if not role:
            return HttpResponse(角色不存在)
        role.permissions.set(request.POST.getlist(permissions))

    # 所有用户,界面用户展示
    user_list = User.objects.all()
    # 取得当前用户的所有角色
    user_has_roles = User.objects.filter(id=uid).values(id, roles)
    # 获取用户拥有的角色id,数据结构是角色id: None,这种数据结构推荐,到时直接in就能判断了,效率高
    user_has_roles_dict = item[roles]: None for item in user_has_roles

    # 角色列表(所有角色),界面用户展示
    role_list = Role.objects.all()

    # 如过选中了角色,那么就根据角色id拿到所有的权限
    if rid:
        role_has_permissions = Role.objects.filter(id=rid).values(id, permissions)
    # 如果只选中了用户没有选择角色,那么就通过用户的角色去拿对应的所有权限
    elif uid and not rid:
        user = User.objects.filter(id=uid).first()
        if not user:
            return HttpResponse(用户不存在)
        role_has_permissions = user.roles.values(id, permissions)
    else:
        # 都没选中,就是初始化状态,界面不勾选任何权限菜单
        role_has_permissions = []

    # 获取角色拥有的权限id,数据结构是权限id: None
    role_has_permissions_dict = item[permissions]: None for item in role_has_permissions

    # 以列表形式存放所有的菜单信息
    all_menu_list = []

    # 查询出所有菜单
    menu_queryset = Menu.objects.values(id, title)
    # 以字典形式存放所有的菜单信息
    menu_dict = 

    # 这个for循环的作用是将一级菜单信息分别放入了menu_dict字典和all_menu_list列表中
    for item in menu_queryset:
        item[children] = []   # 存放二级菜单(父权限)
        menu_dict[item[id]] = item    # 注意这里是将item对象赋值给了item[‘id‘],所以menu_dict和all_menu_list是一起变化的
        all_menu_list.append(item)

    """
    下面是这两个的数据结构,字典套字典,然后children字段子菜单就是列表,然后反复这样嵌套
    menu_dict = ‘menu_id‘: ‘id‘:1, ‘title‘: ‘xxx‘, ‘children‘: [
                ‘id‘, ‘title‘, ‘menu_id‘, ‘children‘: [
                    ‘id‘, ‘title‘, ‘parent_id‘
                ],
                ],
                None: ‘id‘: None, ‘title‘: ‘其他‘, ‘children‘: [‘id‘, ‘title‘, ‘parent_id‘]
                
    all_menu_list = [
        ‘id‘:1, ‘title‘: ‘xxx‘, ‘children‘: [
        ‘id‘, ‘title‘, ‘menu_id‘, ‘children‘: [
            ‘id‘, ‘title‘, ‘parent_id‘
        ],
        ],
        ‘id‘: None, ‘title‘: ‘其他‘, ‘children‘: [‘id‘, ‘title‘, ‘parent_id‘]
    ]
    """
    # 像首页这些不属于任何一级菜单,所以可以归属于other下面
    other = id: None, title: 其他, children: []
    # 两个数据结构分别加入other
    all_menu_list.append(other)
    menu_dict[None] = other

    # 查询二级菜单的权限信息
    parent_permission = Permission.objects.filter(menu__isnull=False).values(id, title, menu_id)
    # 二级菜单信息字典
    parent_permission_dict = 
    """
    parent_permission_dict = 父权限id: ‘id‘, ‘title‘, ‘menu_id‘, ‘children‘: [
        ‘id‘, ‘title‘, ‘parent_id‘
    ] 
    """

    for per in parent_permission:
        per[children] = []    # 存放子权限
        nid = per[id]
        menu_id = per[menu_id]
        # 以二级菜单id为键,二级菜单信息为值加入到二级菜单字典中
        parent_permission_dict[nid] = per
        # 一级菜单字典将二级菜单加入到children下,注意一级菜单列表数据结构也会跟着增加(py内存使用导致)
        menu_dict[menu_id][children].append(per)

    # 类似上面的操作,将不是二级菜单的权限全部找出来,包括子权限和other
    node_permission = Permission.objects.filter(menu__isnull=True).values(id, title, parent_id)

    for per in node_permission:
        pid = per[parent_id]
        # 如果不是子权限,就将信息加入到other的children下
        if not pid:
            menu_dict[None][children].append(per)
            continue
        # 是子权限就加入到二级菜单的children下,因为menu_dict存放的是二级菜单的对象,所以此时menu_dict就有了各个层级的数据
        parent_permission_dict[pid][children].append(per)

    return render(request, rbac/distribute_permissions.html,
                  
                      user_list: user_list,
                      role_list: role_list,
                      user_has_roles_dict: user_has_roles_dict,
                      role_has_permissions_dict: role_has_permissions_dict,
                      all_menu_list: all_menu_list,
                      uid: uid,
                      rid: rid,
                  )

前端代码:

% extends ‘layout.html‘ %
% block css %
    <style>
        .user-area ul 
            padding-left: 20px;
        

        .user-area li 
            cursor: pointer;
            padding: 2px 0;
        

        .user-area li a 
            display: block;
        

        .user-area li.active 
            font-weight: bold;
            color: red;
        

        .user-area li.active a 
            color: red;
        

        .role-area tr td a 
            display: block;
        

        .role-area tr.active 
            background-color: #f1f7fd;
            border-left: 3px solid #fdc00f;
        

        .permission-area tr.root 
            background-color: #f1f7fd;
            cursor: pointer;
        

        .permission-area tr.root td i 
            margin: 3px;
        

        .permission-area .node 

        

        .permission-area .node input[type=‘checkbox‘] 
            margin: 0 5px;
        

        .permission-area .node .parent 
            padding: 5px 0;
        

        .permission-area .node label 
            font-weight: normal;
            margin-bottom: 0;
            font-size: 12px;
        

        .permission-area .node .children 
            padding: 0 0 0 20px;
        

        .permission-area .node .children .child 
            display: inline-block;
            margin: 2px 5px;
        

        table 
            font-size: 12px;
        

        .panel-body 
            font-size: 12px;
        

        .panel-body .form-control 
            font-size: 12px;
        
    </style>
% endblock %
% block content %
    <div class="container-fluid" style="margin-top: 20px">
        <div class="col-sm-3 user-area">
            <div class="panel panel-default">
                <div class="panel-heading"><i class="fa fa-user"></i> 用户信息</div>
                <div class="panel-body">
                    <ul>
                        % for user in user_list %
                            <li class=% if user.id|safe == uid %"active"% endif %>
                                <a href="?uid= user.id "> user.name </a>
                            </li>
                        % endfor %

                    </ul>
                </div>
            </div>
        </div>
        <div class="col-sm-3 role-area">
            <form action="" method="post">
                % csrf_token %
                <input type="hidden" name="postType" value="role">
                <div class="panel panel-default">
                    <div class="panel-heading"><i class="fa fa-book"></i> 角色
                        % if uid %
                            <button type="submit" style="padding: 2px 6px;position: relative;top: -3px;"
                                    class="btn btn-success pull-right"><i style="margin-right: 2px"
                                                                          class="fa fa-save"></i> 保存
                            </button>
                        % endif %
                    </div>
                    <div class="panel-body">
                        <span style="color: darkgray">提示:点击用户后才能为其分配角色</span>
                    </div>
                    <table class="table table-hover">
                        <thead>
                        <tr>
                            <th>角色</th>
                            <th>选择</th>
                        </tr>
                        </thead>
                        <tbody>
                        % load my_tag %
                        % for role in role_list %
                            <tr % if role.id|safe == rid %class="active"% endif %>
                                <td><a href="?% get_role_url request role.id %"> role.name </a></td>
                                <td>
                                    % if role.id in user_has_roles_dict %
                                        <input type="checkbox" name="roles" value=" role.id " checked>
                                    % else %
                                        <input type="checkbox" name="roles" value=" role.id ">
                                    % endif %
                                </td>
                            </tr>
                        % endfor %

                        </tbody>
                    </table>

                </div>
            </form>
        </div>
        <div class="col-sm-6 permission-area">
            <form action="" method="post">
                % csrf_token %
                <input type="hidden" name="postType" value="permission">
                <div class="panel panel-default">
                    <div class="panel-heading"><i class="fa fa-reddit"></i> 权限分配</div>
                    % if rid %
                        <button type="submit"
                                style="padding: 2px 6px;position: relative;top: -32px; margin-right: 10px;"
                                class="btn btn-success pull-right"><i style="margin-right: 2px" class="fa fa-save"></i>
                            保存
                        </button>
                    % endif %
                    <div class="panel-body">
                        <span style="color: darkgray">提示:点击角色后,才能为其分配权限</span>
                    </div>
                    <table class="table">
                        <tbody class="permission-tbody">
                        % for item in all_menu_list %
                            <tr class="root">
                                <td><i class="fa fa-caret-down"></i> item.title </td>
                            </tr>
                            <tr class="node">
                                <td>
                                    % for node in item.children %
                                        <div class="parent">
                                            % if node.id in role_has_permissions_dict %
                                                <input id="permission_ node.id " name="permissions"
                                                       value=" node.id " type="checkbox" checked>
                                            % else %
                                                <input id="permission_ node.id " name="permissions"
                                                       value=" node.id " type="checkbox">
                                            % endif %
                                            <label for="permission_ node.id "> node.title </label>
                                        </div>
                                        <div class="children">
                                            % for child in node.children %
                                                <div class="child">
                                                    % if child.id in role_has_permissions_dict %
                                                        <input id="permission_ child.id " name="permissions"
                                                               value=" child.id " type="checkbox" checked>
                                                    % else %
                                                        <input id="permission_ child.id " name="permissions"
                                                               value=" child.id " type="checkbox">
                                                    % endif %
                                                    <label for="permission_ child.id "> child.title </label>
                                                </div>
                                            % endfor %
                                        </div>
                                    % endfor %
                                </td>
                            </tr>
                        % endfor %
                        </tbody>
                    </table>

                </div>
            </form>
        </div>
    </div>
% endblock %
% block js %
    <script>
        $(.permission-tbody).on(click, .root, function () 
            var caret = $(this).find(i);
            if (caret.hasClass(fa-caret-right)) 
                caret.removeClass(fa-caret-right).addClass(fa-caret-down);
                $(this).next(.node).removeClass(hide);
             else 
                caret.removeClass(fa-caret-down).addClass(fa-caret-right);
                $(this).next(.node).addClass(hide);
            
        )
    </script>

% endblock %

至此,页面大致开发完成,单独开发完成rbac之后,还得嵌入项目中去,大致也说一下迁移的过程:

rbac应用于其他项目流程
1.拷贝rbac到新项目
2.在settings中注册rabc app
3.数据库迁移
    首先先删除原有migrations下的文件,再执行数据库迁移命令
    python manage.py makemigrations
    python manage.py migrate
4.在根目录下的urls.py中添加rbac相关的url
    re_path(r^rbac/, include(rbac.urls, namespace=rbac))
5.layout.html的创建和编写,因为rbac中的模板都继承了这个
6.录入权限信息
    角色管理
    权限管理
7.分配权限
    先用户关联,原系统用户表一对一关联rbac用户表
    from rbac.models import User
    user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
    给用户分角色和权限
8.登录应用权限
    登录成功后获取rbac的user_obj,然后初始化用户信息

    应用权限校验中间件
9.注意模板layout内容和名称

10.应用面包屑导航栏,中间会遇到很多样式js等不同,慢慢调试吧

11.权限控制到按钮级别

 

以上是关于RABC权限控制(页面操作角色,权限和进行分配)的主要内容,如果未能解决你的问题,请参考以下文章

RABC --权限控制解读

为啥用友u8把数据权限分配里面的业务对象没有部门?

用户角色权限控制的杂记

java如何做权限管理

项目一众筹网06_01_权限控制角色和权限分配Admin分配RoleRole分配Auth前端jquery实现列表移动过来移动过去有些表是不需要实体类的自然也就不需要做逆向工程sql语句

将控制器操作权限动态分配给 asp.net MVC 中的角色