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>

posted @ 2019-05-15 09:53  寒菱  阅读(471)  评论(0编辑  收藏  举报