zoukankan      html  css  js  c++  java
  • 权限组件(15):rbac的使用文档和在业务中的应用

    这里用主机管理系统当做示例。

    一、将rbac组件拷贝到项目中。

    注意:

    1.  rbac自己的静态文件、layout.html(被继承的模板)、bootstrap、fontsize、公共的css、jquery、layout里用到的图片也要拷贝进来 
    2. 在settings.py注册rbac: 
    3. 二级菜单和面包屑导航需要先注释掉
    4. 在settings中将LANGUAGE_CODE = 'en-us'改为LANGUAGE_CODE = 'zh-hans'
    在layout.html里注释掉二级菜单和面包屑导航

    <div class="pg-body">
        <div class="left-menu">
            <div class="menu-body">
    {#            {% multi_menu request %}#}
            </div>
        </div>
        <div class="right-body">
    {#        {% breadcrumb request %}#}
            {% block content %} {% endblock %}
        </div>
    </div>

    在settings.py里注册rbac

    INSTALLED_APPS = [
        ...
        'rbac.apps.RbacConfig'
        ...
    ]
    
    

    二、将rbac/migrations目录中的数据库迁移记录删除(init.py不能删除)

    三、业务系统中用户表结构的设计

    业务表结构中的用户表需要和rbac中的用户表有继承关系如:

    • rbac/models.py
    class UserInfo(models.Model):
        """
        用户表
        """
        name = models.CharField(verbose_name='用户名', max_length=32)
        password = models.CharField(verbose_name='密码', max_length=64)
        email = models.CharField(verbose_name='邮箱', max_length=32)
        roles = models.ManyToManyField(verbose_name='拥有的所有角色', to=Role, blank=True)
        # 去掉引号就把Role这个类的内存地址也继承过去了,这样做数据库迁移就不会报错了
    
        def __str__(self):
            return self.name
    
        class Meta:
            # django以后再做数据库迁移时,不再为UserInfo类创建相关的表以及表结构了
            # 此类可以当做"父类",被其他Model类继承。里面的字段就自动过度给继承它的类了
            abstract = True

    注意:

    1. 改成abstract后就不能在admin里注册了
    2. 多对多关联Role的时候不能加引号
    • 业务/models.py
    from rbac.models import UserInfo as RbacUserInfo
    
    class Host(models.Model):
    
        """主机表"""
        hostname = models.CharField(verbose_name='主机名', max_length=32)
        ip = models.GenericIPAddressField(verbose_name='IP', protocol='both')
        department = models.ForeignKey(verbose_name='归属部门', to='Department', on_delete=models.CASCADE)
    
        def __str__(self):
            return self.hostname
            
    class UserInfo(RbacUserInfo):
        """用户表"""
    
        phone = models.CharField(verbose_name='联系方式', max_length=32)
        T1 = 1
        T2 = 2
        T3 = 3
        level_choices = (
            (T1, 'T1'),
            (T2, 'T2'),
            (T3, 'T3')
        )
        level = models.IntegerField(verbose_name='级别', choices=level_choices)
        department = models.ForeignKey(verbose_name='部门', to='Department', on_delete=models.CASCADE)

    迁移数据库

    ./manage.py makemigrations
    ./manage.py migrate

    四、将业务系统中的用户表的路径写到配置文件

    settings.py

    # 业务中的用户表
    RBAC_USER_MODEL_CLASS = 'host.models.UserInfo' # 用于在rbac分配权限时,读取业务表中的用户信息。

    host换成你的业务app

    在权限组件的视图函数中将用户表改为新的,所有的UserInfo都要改成user_model_class

    rbac/views/menu.py

    ...
    from django.conf import settings
    from django.utils.module_loading import import_string
    ...
    
    
    ...
    def distribute_permissions(request):
        """
        权限分配
        :param request:
        :return:
        """
    
        user_id = request.GET.get('uid')
        
        # user_object = models.UserInfo.objects.filter(id=user_id).first()  # 之前的用户表,不要了
    
        # 业务中的用户表
        user_model_class = import_string(settings.RBAC_USER_MODEL_CLASS) # 自动根据字符串的形式,把这个类导入进来
    
        user_object = user_model_class.objects.filter(id=user_id).first()
    ...

     五、业务逻辑开发

    1. 开发一个登陆功能

    2.将所有的路由都设置一个name,用来反向生成url以及粒度控制到按钮级别的权限控制。如:

    from django.contrib import admin
    from django.urls import path, re_path
    
    from host.views import account
    from host.views import user
    from host.views import host
    
    urlpatterns = [
        re_path(r'^admin/', admin.site.urls),
    
        re_path(r'^login/$', account.login, name='login'),
        re_path(r'^logout/$', account.logout, name='logout'),
        re_path(r'^index/$', account.index, name='index'),
    
        re_path(r'^user/list/$', user.user_list, name='user_list'),
        re_path(r'^user/add/$', user.user_add, name='user_add'),
        re_path(r'^user/edit/(?P<pk>d+)/$', user.user_edit, name='user_edit'),
        re_path(r'^user/delete/(?P<pk>d+)/$', user.user_delete, name='user_delete'),
        re_path(r'^user/reset/pwd/(?P<pk>d+)/$', user.user_reset_pwd, name='user_reset_pwd'),
    
        re_path(r'^host/list/$', host.host_list, name='host_list'),
        re_path(r'^host/add/$', host.host_add, name='host_add'),
        re_path(r'^host/edit/(?P<pk>d+)/$', host.host_edit, name='host_edit'),
        re_path(r'^host/delete/(?P<pk>d+)/$', host.host_delete, name='host_delete'),
    
    ]

    六、权限信息的录入

    在url中添加rbac的路由分发。注意:必须设置namesapce

    项目/urls.py

    ...
    from django.urls import path, re_path, include
    ...
    
    urlpatterns = [
     ...
     path('rbac/', include(('rbac.urls', 'rbac'))),
     ...
     ]

    注意:rbac中的用户管理相关的URL配置要注释掉或删除掉

    添加菜单、分配角色、录入权限

    rbac提供的地址进行操作

    菜单列表 - http://127.0.0.1:8000/rbac/menu/list/
    角色列表 - http://127.0.0.1:8000/rbac/role/list/
    权限分配 - http://127.0.0.1:8000/rbac/distribute/permissions/

    1. 先添加一级菜单,然后批量录入二级菜单和权限

    2. 添加完菜单后创建角色

    3. 最后记得分配权限,否则后面加上中间件后会提示无权限访问

    相关配置:自动发现URL时,排除的URL

    AUTO_DISCOVER_EXCLUDE = [
        '/admin/',
        '/login/',
        '/logout/',
        '/index/',
    ]

    注意:如果排除的URL中,如admin、login等用的是django2.0新出的path方法,那么正则匹配的之后需要写成/admin.*,因为path会在admin后面加一个斜杠()。/admin/配置的话就会在自动发现URL中出现一推这样的URL:/admin/

    七、编写用户登录的逻辑【进行权限的初始化】

    def login(request):
        """
        登录
        :param request:
        :return:
        """
        if request.method == 'GET':
            return render(request, 'login.html')
        user = request.POST.get('username')
        pwd = request.POST.get('password')
    
        user_obj = models.UserInfo.objects.filter(name=user, password=pwd).first()
    
        if not user_obj:
            return render(request, 'login.html', {'error': '用户名或密码错误'})
    
        # 用户权限信息的初始化
        init_permission(user_obj, request)
    
        return redirect(reverse('index'))

    相关配置:权限和菜单的session key:

    PERMISSION_SESSION_KEY = 'permission_url_list_key'
    MENU_SESSION_KEY = 'permission_menu_key'

    八、编写一个首页的逻辑

    def index(request):
        return render(request, 'index.html')

    相关配置:

    # 需要登录,但无需权限的URL
    NO_PERMISSION_LIST = [
        '/logout/',
        '/index/',
    ]

    在中间件新增无需权限校验,但是需要登录才能访问的功能

     ...
     url_record = [
                {'title': '首页', 'url': '#'}
            ]
    
            # 此处代码进行判断:/logout/,/index/  无需权限校验,但是需要登录才能访问
            for url in settings.NO_PERMISSION_LIST:
                if re.match(url, request.path_info):
                    # 需要登录,但无需权限校验
                    request.current_selected_permission = 0  # 等于0就是没有默认选中,和菜单没有关联上,就不会做默认展开
                    request.breadcrumb = url_record
                    return None
    
            has_permission = False
    ...

    九、通过中间件进行权限的校验

    rbac/middlewares/rabc.py

    # 权限校验
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    
        'rbac.middlewares.rbac.RbacMiddleware',
    ]

    白名单

    # 白名单,无需登录就可以访问
    WHITE_LIST = ['/login/', '/admin/.*']

    十、粒度到按钮级别的控制

    host/templates/user_list.html

    {% extends 'layout.html' %}
    {% load rbac %}
    
    
    {% block content %}
    
        <h1>用户列表</h1>
        <div class="custom-container">
            <div class="btn-group" style="margin: 5px 0">
                {% if request|has_permission:'user_add' %}  <!-- 控制增加按钮 -->
                    <a class="btn btn-default" href="{% memory_url request 'user_add' %}">
                        <i class="fa fa-plus-square" aria-hidden="true"></i> 添加用户
                    </a>
                {% endif %}
            </div>
    
            <table class="table table-bordered table-hover">
                <thead>
                <tr>
                    <th>用户名</th>
                    <th>邮箱</th>
                    <th>级别</th>
                    <th>部门</th>
                    <th>手机</th>
                    {% if request|has_permission:'user_reset_pwd' %}  <!-- 控制重置密码按钮 -->
                        <th>重置密码</th>
                    {% endif %}
                    {% if request|has_permission:'user_edit' or 'user_delete' %}
                        <th>操作</th>
                    {% endif %}
                </tr>
                </thead>
                <tbody>
                {% for row in user_queryset %}
                    <tr>
                        <td>{{ row.name }}</td>
                        <td>{{ row.email }}</td>
                        <td>{{ row.get_level_display }}</td>
                        <td>{{ row.department.title }}</td>
                        <td>{{ row.phone }}</td>
    
                        {% if request|has_permission:'user_reset_pwd' %}  
                            <td>
                                <a href="{% memory_url request 'user_reset_pwd' pk=row.id %}">重置密码</a>
                            </td>
                        {% endif %}
    
                        {% if request|has_permission:'user_edit' or 'user_delete' %}  <!-- 控制编辑和删除按钮 -->
                            <td>
                                {% if request|has_permission:'user_edit' %}
                                    <a style="color: #333333; font-size:18px"
                                       href="{% memory_url request 'user_edit' pk=row.id %}">
                                        <i class="fa fa-edit" aria-hidden="true"></i></a>
                                {% endif %}
    
                                {% if request|has_permission:'user_delete' %}
                                    <a style="color: red; font-size:18px"
                                       href="{% memory_url request 'user_delete' pk=row.id %}">
                                        <i class="fa fa-trash-o" aria-hidden="true"></i></a>
                                {% endif %}
    
                            </td>
                        {% endif %}
    
                    </tr>
                {% endfor %}
                </tbody>
            </table>
    
        </div>
    
    {% endblock content %}

    总结:目的是希望在任意系统中应用权限系统

    1. 用户登录 + 用户首页 + 用户注销 + 业务逻辑

    2. 业务逻辑开发。注意,开发时要灵活的去设计layout.html中的两个inclusion_tag

    1. 权限信息的录入

    2. 配置文件

    INSTALLED_APPS = [
        ...
        'host.apps.HostConfig',
        'rbac.apps.RbacConfig'
        ...
    ]
    
    MIDDLEWARE = [
        ...
        'rbac.middlewares.rbac.RbacMiddleware',
        ...
    ]
    
    # 业务中的用户表
    RBAC_USER_MODEL_CLASS = 'host.models.UserInfo'
    
    # 权限在session中存储的key
    PERMISSION_SESSION_KEY = 'permission_url_list_key'
    
    # 菜单在Session中存储的key
    MENU_SESSION_KEY = 'permission_menu_key'
    
    # 白名单
    WHITE_LIST = ['/login/', '/admin/.*']
    
    # 自动发现路由中URL时,排除的URL
    AUTO_DISCOVER_EXCLUDE = [
        '/admin.*',
        '/login.*',
        '/logout.*',
        '/index.*',
    ]
    
    # 需要登录,但无需权限的URL
    NO_PERMISSION_LIST = [
        '/logout/',
        '/index/',
    ]
    1. 粒度到按钮级别的控制

  • 相关阅读:
    随笔53 java存在继承关系的类之间的调用
    Servlet 与 CGI 的比较
    Angularjs导出数据到Excel
    JavaScript获得当前月份起止日期
    const与let
    JS判断浏览器类型及版本号(Web端)
    JSON怎么添加注释
    CSS中的特殊的选择器
    CSS界面友好显示的小技巧
    CSS3使用弹性盒子模型定义布局
  • 原文地址:https://www.cnblogs.com/lshedward/p/10546704.html
Copyright © 2011-2022 走看看