权限管理

首先清楚两点:

什么是权限一个包含正则表达式url就是一个权限

 rbac(role-based access control):以角色为基础的权限管理设计

创建步骤:

 一、先创建一个 项目,建一个app01和rbac的应用

在settings中进行配置:

  

 二、设计表结构 

在models.py创建四个类,一共六张表

    用户表:User
    角色表:Role

    权限表:Permission
    权限组表:PermissionGroup
    角色表和权限表是多对多的关系(一个角色可以有多个权限,一个权限可以对应多个角色)role_permissions
    用户表和角色表是多对多的关系(一个用户可以有多个角色,一个角色有多个用户) user_roles

  

from django.db import models


class User(models.Model):
    name=models.CharField(max_length=32)
    pwd=models.CharField(max_length=32)
    roles=models.ManyToManyField(to="Role")

    def __str__(self): return self.name

class Role(models.Model):
    title=models.CharField(max_length=32)
    permissions=models.ManyToManyField(to="Permission")

    def __str__(self): return self.title

class Permission(models.Model):
    title=models.CharField(max_length=32)
    url=models.CharField(max_length=32)

    action=models.CharField(max_length=32,default="")
    group=models.ForeignKey("PermissionGroup",default=1) #一个组有多个权限,外键放在这里
    def __str__(self):return self.title



class PermissionGroup(models.Model):
    title = models.CharField(max_length=32)

    def __str__(self): return self.title
rbac/models.py

具体分析为什么要和权限组表呢在permission表中添加action字段并关联一张permissiongroup表呢?

1、我们一般是先看到的是列表页面,在这个页面上是否显示添加,是否显示编辑,是否显示删除,都是需要判断的
   有无添加权限,有无删除权限,有无编辑权限,我们可以给每一个url一个action

dict = {
    1:{                   action
          /userinfo/            list
       /userinfo/add/       add
       /userinfo/del(\d+)/    del 
       /userinfo/edit(\d+)/    edit
    }
  }

不仅在列表页面需要知道他有那些权限,在其他页面也知道他有哪些权限
所以上面的方案还是有点不好,那么我们采取下面的方案。将action取出来放在一个列表里面

dict = {
      1:{
              "actions":["list","add","del","edit"]
             urls:[
                "/userinfo/",
                "/userinfo/add"/,
                "/userinfo/del(\d+)/ ",
                "/userinfo/edit(\d+)/ ",
              ]    
        }
      2:{
           "codes":{"list","add","del","edit"}
            urls:[
                 "/order",
                 "/order/add"/,
                  "/order/del(\d+)/ ",
                 "/order/edit(\d+)/ ",
               ]    
       }
}   

把这个字典存到session中
当你访问页面的时候我就知道你有什么权限
一个url对应一个action
多个url对应一个组

注意:
  关联字段 null = True        数据库用的时候可以为空
  关联字段 blank = True     admin用的时候可以为空

三、通过django-admin录入权限数据

- 先创建一个超级用户 python3 manage.py createsuperuser
- 用户名 zh
- 密码 zh123456

注册表

#在rbac/models.py中注册表
from django.contrib import admin
from .models import *

class PerConfig(admin.ModelAdmin):
    list_display = ["title","url","group","action"]
admin.site.register(User)
admin.site.register(Role)
admin.site.register(Permission,PerConfig)
admin.site.register(PermissionGroup)

四、编写登录

1.编写登录
2.如果用户验证成功就设置session
3.先查出当前用户的所有的权限,注册到session中

from rbac.service.perssions import *

def login(request):

    if  request.method=="POST":

        user=request.POST.get("user")
        pwd=request.POST.get("pwd")
        user=User.objects.filter(name=user,pwd=pwd).first()
        #如果登录成功
        if user:
            #在session中注册用户ID
            request.session["user_id"]=user.pk

            # 查询当前登录用户的所有权限,注册到session中
            initial_session(user,request)

            return HttpResponse("登录成功!")

    return render(request,"login.html")
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<h4>登录页面</h4>

<form action="" method="post">
    {% csrf_token %}
    
    用户名:<input type="text" name="user">
    密码:<input type="password" name="pwd">
    <input type="submit">
</form>


</body>
</html>
login.html

rbac/service/permissions.py

# 查询当前登录用户的所有权限,注册到session中,以下打印以egon登录为例,他只有一个查看的权限;/users/
def initial_session(user,request):
    # #方案1 拿到权限列表,直接注册到session中
    # permissions = user.roles.all().values("permissions__url").distinct()
    # # [{},{}]
    # permission_list = []
    #
    # for item in permissions:
    #     permission_list.append(item["permissions__url"])
    # print(permission_list)   # ['/users/']
    #
    # request.session["permission_list"] = permission_list

    ##方案2
    #重构数据结构 (已重构表结构 :为Permission表加了一个字段action,外键关联一个表group)
    permissions = user.roles.all().values("permissions__url","permissions__group_id","permissions__action").distinct()
    print("permissions",permissions) #查看egon的权限为例它只有一个权限,列表中只有一个字典
    #permissions <QuerySet [{'permissions__url': '/users/', 'permissions__group_id': 1, 'permissions__action': 'list'}]>

    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)     #{
    #                             1: {
    #                           'urls': ['/users/'],
    #                           'actions': ['list']}},        构建的数据结构: 字典中-以组号为键,字典为值
    request.session['permission_dict']=permission_dict

    # 注册菜单权限
    permissions = user.roles.all().values("permissions__url","permissions__action","permissions__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__title"]))

    print(menu_permission_list)
    request.session["menu_permission_list"]=menu_permission_list

通过以上设置,在用户登录成功后我们就能通过requset.actions查看用户有哪些操作权限。

五、基于中间件的权限校验

在settings中添加中间件

 

  

 rbac/service/rbac.py

class ValidPermission(MiddlewareMixin):

    def process_request(self,request):

        # 拿到当前访问的路径,登录为例
        current_path = request.path_info
        print(current_path)   #/login/

        # 检查是否属于白名单,不然连登录注册的界面都进不去
        valid_url_list=["/login/","/reg/","/admin/.*"]
        # "/admin/.*" 的 .* 表示只要是admin开头就是,后面不管跟什么,因为我们在访问
        #/admin/的时候会自动跳转到 /admin/login/?next=/admin/ 这个路径下

        #如果访问的路径属于白名单,正常访问
        for valid_url in valid_url_list:
            ret=re.match(valid_url,current_path)
            if ret:
                return None

        # 校验是否登录,这个user_id是用户登录时注册到session中的
        user_id=request.session.get("user_id")
        #如果没有登录,返回登录界面
        if not user_id:
            return redirect("/login/")

        ##校验权限2

        permission_dict=request.session.get("permission_dict")

        for item in permission_dict.values():   #循环字典里面的值  也就是字典 {1: { 'urls': ['/users/'],'actions': ['list']}}中键1所对应的值
              urls=item['urls']                    #取出urls的值['/users/']
              for reg in urls:                     #循环取出['/users/'] 里面的值,列表里面不止一个值,在这里登录人只有一个权限所以只有一个值
                  reg="^%s$"%reg                   #reg ="^/users/$"
                  ret=re.match(reg,current_path)   #和当前访问路径做匹配,也就是登录成功后所访问的路径,这里以访问/users/为例
                  if ret:
                      print("actions",item['actions'])    #actions ['list']
                      request.actions=item['actions']     #重构数据结构只为拿到用户对访问的表拥有增删改查的哪些权限,添加到request中
                      return None   #匹配成功,正常访问

        return HttpResponse("没有访问权限!")

rbac/templatetags/my_tags.py

 

from django import template

register=template.Library()

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

    return {"menu_permission_list":menu_permission_list}

 

六、表单的增删改以及查看权限

app01/views.py

from django.shortcuts import render,HttpResponse

from rbac.models import *

class Per(object):
    def __init__(self,actions):
        self.actions=actions
    def add(self):
        return "add" in self.actions
    def delete(self):
        return "delete" in self.actions
    def edit(self):
        return "edit" 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")
    #print(permission_list)

    # 查询当前登录人得名字

    id=request.session.get("user_id")
    user=User.objects.filter(id=id).first()

    per=Per(request.actions)

    return render(request,"rbac/users.html",locals())


import re
def add_user(request):

    return HttpResponse("add user.....")

def del_user(request,id):

    return HttpResponse("del"+id)

def roles(request):

    role_list=Role.objects.all()
    per = Per(request.actions)
    return render(request,"rbac/roles.html",locals())


from rbac.service.perssions import *

def login(request):

    if  request.method=="POST":

        user=request.POST.get("user")
        pwd=request.POST.get("pwd")

        user=User.objects.filter(name=user,pwd=pwd).first()
        #如果登录成功
        if user:
            #在session中注册用户ID
            request.session["user_id"]=user.pk

            # 查询当前登录用户的所有权限,注册到session中
            initial_session(user,request)

            return HttpResponse("登录成功!")

    return render(request,"login.html")

 相关html文件

rabc/templates/rabc/base.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .header{
            width: 100%;
            height: 60px;
            background-color: #336699;
        }

        .menu{
            background-color: bisque;
            position: fixed;
            top: 60px;
            bottom: 0px;
            left: 0px;
            width: 200px;
        }

        .content{
            position: fixed;
            top: 60px;
            bottom: 0;
            right: 0;
            left: 200px;
            overflow: auto;
            padding: 30px;

        }

        .menu_btn{
            font-size: 15px;
            text-align: center;
            padding: 30px 0;

        }

        .header .title{
            color: white;
            font-size: 18px;
            margin-right: 20px;
            font-weight: 100;
        }
    </style>
</head>
<body>

<div class="header">
    <p class="title pull-right">{{ user.name }}</p>
</div>
<div class="contain">
        {% load my_tags %}
        <div class="menu">
             {% get_menu request %}
        </div>
        <div class="content">
        {% block con %}

        {% endblock %}

        </div>
</div>

</body>
</html>

rabc/templates/rabc/menu.html

<div>

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

</div>

rabc/templates/rabc/users.html

{% extends 'base.html' %}

{% block con %}

    <h4>用户列表</h4>
{#    {% if "users/add" in permissions_list %} 在重构数据结构后这一句用下面一句代替#}
    {% if per.add %}    <!--如果有添加用户的权限就显示添加用户的按钮-->
    <a href="/users/add/" class="btn btn-primary">添加用户</a>
    {% endif %}
    <table class="table table-bordered table-striped">
        <thead>
              <tr>
                   <th>序号</th>
                   <th>姓名</th>
                   <th>角色</th>
                   <th>操作</th>
              </tr>
        </thead>
       <tbody>
            {% for user in user_list %}
            <tr>
                 <td>{{ forloop.counter }}</td>
                 <td>{{ user.name }}</td>
                 <td>
                     {% for role in user.roles.all %}
                     {{ role.title }}
                     {% endfor %}
                 </td>

                 <td>
                     <a href="/users/delete/{{ user.pk }}" class="btn btn-danger">删除</a>
                     <a href="" class="btn btn-warning">编辑</a>
                 </td>
            </tr>
            {% endfor %}

       </tbody>
    </table>

{% endblock %}

rabc/templates/rabc/role.html

{% extends 'base.html' %}

{% block con %}
    <h4>角色列表</h4>

    {% if per.add %}
        <a href="" class="btn btn-primary">添加角色</a>
    {% endif %}

    <table class="table table-bordered table-striped">
        <tbody>
        {% for role in role_list %}
            <tr>
                <td>{{ forloop.counter }}</td>
                <td>{{ role.title }}</td>

                <td>
                    <a href="/users/delete/{{ user.pk }}" class="btn btn-danger">删除</a>
                    <a href="" class="btn btn-warning">编辑</a>
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>

{% endblock %}

以上所用url

from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^users/$', views.users),
    url(r'^users/add', views.add_user),
    url(r'^users/delete/(\d+)', views.del_user),
    url(r'^roles/', views.roles),
    url(r'^login/', views.login),
]

相关博客1

相关博客2

posted @ 2020-05-15 18:07  zh_小猿  阅读(285)  评论(0编辑  收藏  举报