RBAC权限系统实现步骤-初版

一、创建并注册APP

1. 创建App: python mange.py startapp rbac
2. 注册APP:setting.py  
    INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'web.apps.WebConfig',
    'rbac.apps.RbacConfig'
    ]

 

二、设计表结构

from django.db import models
# Create your models here.
"""
RBAC涉及到5张表
    1. 用户表  (用户和角色是多对多的关系)
    2. 角色表  (角色和权限是多对多的关系)
    3. 权限表  
    4. 用户-角色表
    5. 角色-权限表
"""


class UserInfo(models.Model):
    """
    用户表
    roles 字段: 用户和角色多对多字段,这个字段可以写在用户表或者角色表中,主要看场景是根据角色找用户还是根据用户找角色,
                在这个场景中,我们根据用户找角色多,所以我们把ManyToManyField字段写在用户表中
        null = True : 允许为空
        blank = True: 在django Admin后台允许为空
    """
    username = models.CharField(max_length=32, verbose_name="用户名")
    password = models.CharField(max_length=64, verbose_name="密码")
    roles = models.ManyToManyField(to="Role", null=True, blank=True, verbose_name="用户角色")

    def __str__(self):
        """
        在Django Admin中显示数据名称
        :return:
        """
        return self.username

    class Meta:
        """
        在Django Admin中显示中文表名
        """
        verbose_name = "用户表"
        verbose_name_plural = verbose_name


class Role(models.Model):
    """
    角色权限表
    Permissions: 角色和权限URL是多对多的关系,在这个场景中我们使用角色查找URL,所以我们把ManyToManyField写在角色表中
    """
    name = models.CharField(max_length=36, verbose_name="角色名称")
    Permissions = models.ManyToManyField(to="Permission", verbose_name="权限URL")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = "角色表"
        verbose_name_plural = verbose_name


class Permission(models.Model):
    """
    权限表
    保存需要控制的URL
    """
    name = models.CharField(max_length=16, verbose_name="UR名称")
    url = models.CharField(max_length=255, verbose_name="URL路径")
    is_menus = models.BooleanField(default=False, verbose_name="是否可作为菜单?")
    icon = models.CharField(max_length=255, verbose_name="菜单图标", null=True, blank=True)

    def __str__(self):
        return self.url

    class Meta:
        verbose_name = "权限表"
        verbose_name_plural = verbose_name

 

三、创建表结构

python manage.py makemigrations
python manage.py migrate

 

四、使用Django Admin创建初始数据

1. python manage.py createsuperuser
2. 配置Django 后台中文(settings.py)
    LANGUAGE_CODE = 'zh-hans'
3. 注册表到Admin中(admin.py)
    from django.contrib import admin
    from rbac import models

    # Register your models here.

    admin.site.register(models.UserInfo)
    admin.site.register(models.Role)


    # 自定义一个权限的管理类
    class PermissionAdmin(admin.ModelAdmin):
        # 告诉Django admin在页面上展示我这张表的哪些字段
        list_display = ["name", "url", "is_menus", "icon"]
        # 在列表页面支持直接修改的字段
        list_editable = ["url", "is_menus", "icon"]


    admin.site.register(models.Permission, PermissionAdmin)
4. 录入基础数据

 

五、写登录页面

#!/usr/bin/env python3
from django.shortcuts import render, redirect
from rbac import models
from rbac.services import permission


def login(request):
    """
    用户登录页面
        1. Get请求
            1. 返回登录页面
        2. Post请求
            1. 拿到页面通过post传过来的用户名和者密码
            2. 使用orm进行过滤查找
                1. 如果能找到值,则说明登录成功
                    1. 登录成功以后调用rbac函数初始化
                    2. 初始化的主要功能是获取用户的权限和菜单保存到session中
                    3. 跳转到客户列表页面
                2. 登录失败,返回错误信息给页面展示
    :param request: 
    :return: 
    """
    msg = {"msg": ""}
    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")
        user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
        if user_obj:
            permission.init_permission(request, user_obj)
            return redirect("/customer/list/")
        else:
            msg["msg"] = "用户名或者密码错误!"

    return render(request, "login.html", locals())

 

六、写初始化函数(init_permission)

函数功能:根据登录的用户对象取到对象的权限和菜单保存到session中
#!/usr/bin/env python3
"""
用户登录成功以后,获取用户的的权限进行
"""
from django.conf import settings


def init_permission(request, user_obj):
    """
    初始化rbac
        1. 根据用户对象,取到用户对应的角色,根据角色在取到对应的权限。最后distinct做一个去重处理
        2. permission_list 定义一个用来存储用户权限的列表
        3. menus_list 定义一个用来存储用户菜单的列表
        4. 循环ret,添加权限和菜单到对应的列表
        5. 保存权限列表和菜单列表到session中
        备注:使用settings来存session的key方便在其他模块中调用
    :param request: 用户请求对象
    :param user_obj: 用户orm对象
    :return:
    """
    ret = user_obj.roles.all().values(
        "Permissions__name",
        "Permissions__url",
        "Permissions__is_menus",
        "Permissions__icon"
    ).distinct()

    permission_list = []
    menus_list = []
    
    for item in ret:
        permission_list.append({"Permissions__url": item["Permissions__url"]})  # 添加到权限列表
        if item["Permissions__is_menus"]:  # 如果当前循环的权限可以作为菜单展示
            menus_list.append({  # 把当前权限的信息添加到菜单列表
                "name": item["Permissions__name"],
                "icon": item["Permissions__icon"],
                "url": item["Permissions__url"]
            })
    request.session[settings.PERMISSION_SESSION_KEY] = permission_list
    request.session[settings.MENU_SESSION_KEY] = menus_list

 

七、在登录视图中调用init_permission

#!/usr/bin/env python3
from django.shortcuts import render, redirect
from rbac import models
from rbac.services import permission


def login(request):
    """
    用户登录页面
        1. Get请求
            1. 返回登录页面
        2. Post请求
            1. 拿到页面通过post传过来的用户名和者密码
            2. 使用orm进行过滤查找
                1. 如果能找到值,则说明登录成功
                    1. 登录成功以后调用rbac函数初始化
                    2. 初始化的主要功能是获取用户的权限和菜单保存到session中
                    3. 跳转到客户列表页面
                2. 登录失败,返回错误信息给页面展示
    :param request: 
    :return: 
    """
    msg = {"msg": ""}
    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")
        user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
        if user_obj:
            permission.init_permission(request, user_obj)
            return redirect("/customer/list/")
        else:
            msg["msg"] = "用户名或者密码错误!"

    return render(request, "login.html", locals())

 

八、自定义中间件

1. 在rbac APP下创建middleware中间库目录
2. 创建中间件rbac.py
    """
    自定义RBAC中间件
    功能描述:
        根据用户角色实现权限控制
    """
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render, redirect, HttpResponse
    import re
    from django.conf import settings


    class RBACMiddleware(MiddlewareMixin):
        def process_request(self, request):
            """
            自定义中间件
            1. 中间件的描述
                1. 执行时间
                    在执行视图函数之前执行
                2. 执行顺序
                    按照注册的顺序执行
                3. 参数和返回值
                    1. request参数和视图函数中是同一个对象
                    2. 返回值:
                        1. 返回None:请求继续往后执行
                        2. 返回响应对象:请求就结束了,要返回响应了
            2. 取到用户的url
                1. 循环白名单
                2. 判断用户当前访问的URL是否在白名单中
                3. 如果在白名单中则返回None代码继续往后执行
            3. 取到用户的访问权限
                1. 如果没有取到登录时存的session,则说明用户没有登录,跳转到登录页面
            :param request:
            :return:
            """
            url = request.path_info

            for i in settings.PERMISSION_WHITE_URL:
                ret = "^{}$".format(i)
                if re.match(ret, url):
                    return None

            user_url = request.session.get(settings.PERMISSION_SESSION_KEY)
            if not user_url:
                return redirect("/login/")

            for i in user_url:
                ret = "^{}$".format(i["Permissions__url"])
                if re.match(ret, url):
                    return None
                else:
                    return HttpResponse("没有权限方法")

3. 注册中间件settings.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.middleware.rbac.RBACMiddleware',
    ]
    
4. 权限控制已经完成

 

九、开始配置菜单

方法1:
    直接在菜单html修改菜单html为:
    {% for menu in request.session.menu_list %}
        <a href="{{ menu.url }}" class="active">
        <span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.name }}</a>
    {% endfor %}
方法2:
    使用模版语言的filter
    1. 修改菜单html
        {% load view %}
        {% show_menu request %}
    2. 创建app/templatetags/view.py
        from django import template
        from django.conf import settings

        register = template.Library()


        @register.inclusion_tag(filename="my_menu.html")
        def show_menu(request):
            menu_list = request.session.get(settings.MENU_SESSION_KEY)
            return {"menu_list": menu_list}
    3. 创建app/templates/my_menu.html
        <div class="static-menu">
            {% for menu in menu_list %}
                <a href="{{ menu.url }}" class="active">
                    <span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.name }}</a>
            {% endfor %}
        </div>

 

posted @ 2018-09-19 09:53  S.Curry  阅读(1690)  评论(0编辑  收藏  举报