首先清楚两点:
什么是权限:一个包含正则表达式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
具体分析为什么要和权限组表呢在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>
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
通过以上设置,在用户登录成功后我们就能通过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("没有访问权限!")
六、表单的怎删改以及查看权限
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() id=request.session.get("user_id") user=User.objects.filter(id=id).first() per=Per(request.actions) return render(request,"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,"roles.html",locals())
相关html文件
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> .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; } </style> </head> <body> <div class="header"> <p>{{ user.name }}</p> </div> <div class="contain"> <div class="menu">11</div> <div class="content"> {% block con %} {% endblock %} </div> </div> </body> </html>
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 %}
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), ]
原文:https://www.cnblogs.com/zh-xiaoyuan/p/12878137.html