zoukankan      html  css  js  c++  java
  • django权限组件开发

    什么是权限?
    权限 就是对 软件系统 中 各种资源 的 访问和操作的控制!

    什么是资源?
    在软件系统中,数据库、内存、硬盘里数据都是资源,资源就是数据!

    动作
    资源本身是静态的, 必须通过合适的动作对其进行访问和操作,我们说要控制权限,其实本质上是要对访问 软件中各种数据资源的动作进行控制

    动作又可以分为2种:

    1. 资源操作动作:访问和操作各种数据资源,比如访问数据库或文件里的数据

    2. 业务逻辑事件动作:访问和操作的目的不是数据源本身,而是借助数据源而产生的一系列业务逻辑,比如批量往远程 主机上上传一个文件,你需要从数据库中访问主机列表,但你真正要操作的是远程的主机,这个远程的主机,严格意义上来并不是你的数据资源,而是这个资源代表的实体。

    权限授权

    1. 权限的使用者可以是具体的个人、亦可以是其它程序, 这都没关系,我们可以把权限的授权主体,统称为用户, 无论这个用户后面是具体的人,还是一个程序,对权限控制组件来讲,都不影响 。
    2. 权限必然是需要分组的,把一组权限 分成一个组,授权给特定的一些用户,分出来的这个组,就可以称为角色。
    3. 权限 应该是可以叠加的!

    权限表结构设计

    rbac(Role-Based Access Control)基于角色的权限访问控制
    一个人可以有多个角色。
    一个角色可以有多个人。
    一个角色可以有多个权限。
    一个权限可以分配给多个角色。

    from django.db import models
    
    
    class Permission(models.Model):
        """
        权限表
        """
        title = models.CharField(verbose_name='标题', max_length=32)
        url = models.CharField(verbose_name='含正则的URL', max_length=128)
    
        def __str__(self):
            return self.title
    
    
    class Role(models.Model):
        """
        角色
        """
        title = models.CharField(verbose_name='角色名称', max_length=32)
        permissions = models.ManyToManyField(verbose_name='拥有的所有权限', to='Permission', blank=True)
    
        def __str__(self):
            return self.title
    
    
    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)
    
        def __str__(self):
            return self.name
    

    权限校验

    在用户成功登录之后, 从权限表中取出其权限, 存入session中, 在用户访问url的时候进行校验, 判断用户是否有权限

    权限注入

    def login(request):
        if request.method == "POST":
    
            user = request.POST.get("name")
            pwd = request.POST.get("pwd")
    
            user = User.objects.filter(name=user, password=pwd).first()
            if user:
                ############################### 在session中注册用户ID######################
                request.session["user_id"] = user.pk
    
                ###############################在session注册权限列表##############################
    
                # 查询当前登录用户的所有角色
                # ret=user.roles.all()
                # print(ret)# <QuerySet [<Role: 保洁>, <Role: 销售>]>
    
                # 查询当前登录用户的所有权限
                initial_session(user, request)
    
                return HttpResponse("登录成功!")
    
        return render(request, "login.html")
    
    
    def initial_session(user,request):
        permissions = user.roles.all().values("permissions__url").distinct()
    
        permission_list = []
    
        for item in permissions:
            permission_list.append(item["permissions__url"])
        print(permission_list)
    
        request.session["permission_list"] = permission_list
    

    权限校验

    在需要进行权限控制的视图函数中判断是否有权限

        permission_list = request.session['permission_list']
        
        current_path = request.path_info
        flag = False
        for permission in permission_list:
            permission = "^%s$"%permission
            
            ret = re.match(permission, current_path)
            if ret:
                flag = True
                break
        if not flag:
            return HttpResponse("没有访问权限")
    

    基于中间件的权限校验

    有些url是不需要进行权限控制的, 如login, logout, admin, reg
    可以设置一个白名单, 将这些不需要进行权限控制的url放在白名单中

    import re
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import  HttpResponse,redirect
    
    class ValidPermission(MiddlewareMixin):
    
        def process_request(self,request):
    
    
            # 当前访问路径
            current_path = request.path_info
    
            # 检查是否属于白名单
            valid_url_list=["/login/","/reg/","/admin/.*"]
    
            for valid_url in valid_url_list:
                ret=re.match(valid_url,current_path)
                if ret:
                    return None
    
    
            # 校验是否登录
    
            user_id=request.session.get("user_id")
    
            if not user_id:
                return redirect("/login/")
    
    
            # 校验权限
            permission_list = request.session.get("permission_list",[])  # ['/users/', '/users/add', '/users/delete/(\d+)', 'users/edit/(\d+)']
    
            flag = False
            for permission in permission_list:
    
                permission = "^%s$" % permission
    
                ret = re.match(permission, current_path)
                if ret:
                    flag = True
                    break
            if not flag:
                return HttpResponse("没有访问权限!")
    
            return None
    

    将这个中间件注入到MIDDLEWARE中即可实现简单的权限访问控制

    后台admin的控制

    最简单的一种

    视图函数中获取权限列表, 将其传至模板中, 在模板中来进行判断是否显示按钮,
    这样当不同的用户进行访问的时候, 看到的就是不同的页面

    def users(request):
        user_list = User.objects.all()
        permission_list = request.session.get("permission_list")
        id = request.session.get("user_id")
        user = User.objects.filter(id=id).first()
    
        return render(request, "users.html", locals())
    
        {% if '/users/add/' in permission_list %}
            <a href="/users/add/" class="btn btn-primary">添加用户</a>
        {% endif %}
    

    修改models

    给权限添加一个动作(增删改查), 再给权限分个组(一个组管一个model)

    class Permission(models.Model):
        """
        权限表
        """
        title = models.CharField(verbose_name='标题', max_length=32)
        url = models.CharField(verbose_name='含正则的URL', max_length=128)
    
        action = models.CharField(max_length=32, default="", verbose_name="动作", help_text="add, del, change等")
        group = models.ForeignKey("PermissionGroup", blank=True, null=True, verbose_name="权限组", on_delete=models.CASCADE)
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = "权限"
            verbose_name_plural = verbose_name
    
    
    class PermissionGroup(models.Model):
        title = models.CharField(max_length=32, verbose_name="权限组名称")
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = "权限组"
            verbose_name_plural = verbose_name
    

    修改一下登录成功时的session设置

    def initial_session(user, request):
        # permissions = user.roles.all().values("permissions__url").distinct()
        #
        # permission_list = []
        #
        # for item in permissions:
        #     permission_list.append(item["permissions__url"])
        # print(permission_list)
        #
        # request.session["permission_list"] = permission_list
    
        permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct()
        print("permissions", permissions)
    
        permission_dict = {}
        for item in permissions:
            gid = item.get('permissions__group_id')
    
            if not gid in permission_dict:
    
                permission_dict[gid] = {
                    "urls": [item["permissions__url"], ],
                    "actions": [item["permissions__action"], ]
                }
            else:
                permission_dict[gid]["urls"].append(item["permissions__url"])
                permission_dict[gid]["actions"].append(item["permissions__action"])
    
        print(permission_dict)
        request.session['permission_dict'] = permission_dict
    

    中间件也需要修改一下

    class ValidPermission(MiddlewareMixin):
    
        def process_request(self, request):
    
            # 当前访问路径
            current_path = request.path_info
    
            # 检查是否属于白名单
            valid_url_list = ["/login/", "/reg/", "/admin/.*"]
    
            for valid_url in valid_url_list:
                ret = re.match(valid_url, current_path)
                if ret:
                    return None
    
            # 校验是否登录
    
            user_id = request.session.get("user_id")
    
            if not user_id:
                return redirect("/login/")
    
            # 校验权限
            # permission_list = request.session.get("permission_list", [])  # ['/users/', '/users/add', '/users/delete/(\d+)', 'users/edit/(\d+)']
    
            # flag = reg(request, current_path)
            #
            # if not flag:
            #     return HttpResponse("没有访问权限")
    
    
            # 校验权限2
            permission_dict=request.session.get("permission_dict")
    
            for item in permission_dict.values():
                  urls=item['urls']
                  for reg in urls:
                      reg="^%s$"%reg
                      ret=re.match(reg,current_path)
                      if ret:
                          print("actions",item['actions'])
                          request.actions=item['actions']
                          return None
    
            return HttpResponse("没有访问权限!")
    

    在视图函数中进行一个简单的设置

    class Per(object):
        def __init__(self,actions):
            self.actions=actions
        def add(self):
            return "add" in self.actions
        def delete(self):
            return "del" in self.actions
        def edit(self):
            return "change" in self.actions
        def list(self):
            return "list" in self.actions
    
    
    def users(request):
        user_list = User.objects.all()
        # permission_list = request.session.get("permission_list")
        id = request.session.get("user_id")
        user = User.objects.filter(id=id).first()
    
        per = Per(request.actions)
        return render(request, "users.html", locals())
    

    模板中的使用方法

    
    {% if per.delete %}
        <a href="" class="btn btn-danger">删除</a>
    {% endif %}
    {% if per.edit %}
        <a href="" class="btn btn-warning">编辑</a>
    {% endif %}
    

    这样也能实现访问控制

    菜单显示

    initial_session函数中注入菜单

        # 注册菜单权限
        permissions = user.roles.all().values("permissions__url","permissions__action","permissions__group__title").distinct()
        print("permissions",permissions)
    
        menu_permission_list=[]
        for item in permissions:
            if item["permissions__action"]=="list":
                menu_permission_list.append((item["permissions__url"],item["permissions__group__title"]))
    
        print(menu_permission_list)
        request.session["menu_permission_list"]=menu_permission_list
    

    编写一个生成菜单的标签

    @register.inclusion_tag("perm/menu.html")
    def get_menu(request, ):
        # 获取当前用户可以放到菜单栏中的权限
        menu_permission_list = request.session["menu_permission_list"]
    
        return {"menu_permission_list": menu_permission_list}
    

    menu.html

    <div>
        {% for item in menu_permission_list %}
            <p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
        {% endfor %}
    </div>
    

    base模板中的使用方法

        <div class="menu">
            {% get_menu request %}
        </div>
    

  • 相关阅读:
    Finding Palindromes POJ
    吉哥系列故事——完美队形II HDU
    Period II FZU
    生日礼物&&Supermarket
    炮兵阵地[状态压缩DP]
    最小表示法 P1368
    Period
    最长异或路径
    Luogu P5490 扫描线
    解方程
  • 原文地址:https://www.cnblogs.com/gaoyongjian/p/10867693.html
Copyright © 2011-2022 走看看