271 第九篇:BBS项目02:基于角色权限管理:rbac具体代码实现
1、settings
1 """ 2 Django settings for day80 project. 3 4 Generated by 'django-admin startproject' using Django 1.11.6. 5 6 For more information on this file, see 7 https://docs.djangoproject.com/en/1.11/topics/settings/ 8 9 For the full list of settings and their values, see 10 https://docs.djangoproject.com/en/1.11/ref/settings/ 11 """ 12 13 import os 14 15 # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 18 19 # Quick-start development settings - unsuitable for production 20 # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 21 22 # SECURITY WARNING: keep the secret key used in production secret! 23 SECRET_KEY = '7x(f$0j0c#)hn)8i(uv6j*sg^h_7v9$eak#pu_n4ji05=v28ca' 24 25 # SECURITY WARNING: don't run with debug turned on in production! 26 DEBUG = True 27 28 ALLOWED_HOSTS = [] 29 30 31 # Application definition 32 33 INSTALLED_APPS = [ 34 'django.contrib.admin', 35 'django.contrib.auth', 36 'django.contrib.contenttypes', 37 'django.contrib.sessions', 38 'django.contrib.messages', 39 'django.contrib.staticfiles', 40 'app01.apps.App01Config', 41 'rbac', 42 ] 43 from django.middleware.common import CommonMiddleware 44 MIDDLEWARE = [ 45 'django.middleware.security.SecurityMiddleware', 46 'django.contrib.sessions.middleware.SessionMiddleware', 47 'django.middleware.common.CommonMiddleware', 48 'django.middleware.csrf.CsrfViewMiddleware', 49 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 'django.contrib.messages.middleware.MessageMiddleware', 51 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 'rbac.middlewear.rbac.Middle', 53 ] 54 55 ROOT_URLCONF = 'day80.urls' 56 57 TEMPLATES = [ 58 { 59 'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 'DIRS': [os.path.join(BASE_DIR, 'templates')] 61 , 62 'APP_DIRS': True, 63 'OPTIONS': { 64 'context_processors': [ 65 'django.template.context_processors.debug', 66 'django.template.context_processors.request', 67 'django.contrib.auth.context_processors.auth', 68 'django.contrib.messages.context_processors.messages', 69 ], 70 }, 71 }, 72 ] 73 74 WSGI_APPLICATION = 'day80.wsgi.application' 75 76 77 # Database 78 # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 79 80 DATABASES = { 81 'default': { 82 'ENGINE': 'django.db.backends.sqlite3', 83 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 84 } 85 } 86 87 88 # Password validation 89 # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 90 91 AUTH_PASSWORD_VALIDATORS = [ 92 { 93 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 94 }, 95 { 96 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 97 }, 98 { 99 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 100 }, 101 { 102 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 103 }, 104 ] 105 106 107 # Internationalization 108 # https://docs.djangoproject.com/en/1.11/topics/i18n/ 109 110 LANGUAGE_CODE = 'en-us' 111 112 TIME_ZONE = 'UTC' 113 114 USE_I18N = True 115 116 USE_L10N = True 117 118 USE_TZ = True 119 120 121 # Static files (CSS, JavaScript, Images) 122 # https://docs.djangoproject.com/en/1.11/howto/static-files/ 123 124 STATIC_URL = '/static/' 125 # ==========静态文件配置========= 126 STATICFILES_DIRS = ( 127 os.path.join(BASE_DIR,'static'), 128 ) 129 # ==========rbac============ 130 PERMISSION_URL_DICT = "permission_url_list" 131 PERMISSION_MENU_KEY = "permission_menu_list" 132 133 # =========白名单(不需要权限就可以看到的)========== 134 WHITE_LIST =[ 135 "/login/", 136 "/admin.*", 137 "/index/", 138 ]
2、rbac应用下的models
1 from django.db import models 2 3 # Create your models here. 4 class Role(models.Model): 5 title = models.CharField(max_length=32,verbose_name="角色") 6 permissions = models.ManyToManyField(to="Permission",verbose_name="拥有权限的角色",blank=True) #权限和角色是多对多的关系 7 8 def __str__(self): 9 return self.title 10 class Meta: 11 verbose_name_plural = "角色表" 12 13 class Permission(models.Model): 14 title = models.CharField(max_length=32,verbose_name="权限名") 15 url = models.CharField(max_length=32,verbose_name="带正则的url") 16 codes = models.CharField(max_length=32,verbose_name="代码") 17 group = models.ForeignKey(to="Group",verbose_name="所属组",blank=True) #组和权限是一对多的关系,一个组有多个权限 18 menu_gp = models.ForeignKey(to='Permission',related_name='aaa',null=True,blank=True,verbose_name="组内菜单") 19 def __str__(self): 20 return self.title 21 class Meta: 22 verbose_name_plural = "权限表" 23 24 class UserInfo(models.Model): 25 name = models.CharField(max_length=32,verbose_name="姓名") 26 password = models.CharField(max_length=64,verbose_name="密码") 27 email = models.CharField(max_length=32,verbose_name="邮箱") 28 roles = models.ManyToManyField(to="Role",blank=True) #用户和角色是多对多的关系 29 def __str__(self): 30 return self.name 31 class Meta: 32 verbose_name_plural = "用户表" 33 34 class Group(models.Model): 35 title = models.CharField(max_length=32,verbose_name="组名称") 36 menu = models.ForeignKey(to="Menu",verbose_name="组内菜单",blank=True) #一个组下有多个菜单 37 def __str__(self): 38 return self.title 39 class Meta: 40 verbose_name_plural = "组" 41 42 class Menu(models.Model): 43 caption = models.CharField(max_length=32,verbose_name="菜单") 44 def __str__(self): 45 return self.caption 46 class Meta: 47 verbose_name_plural = "菜单表"
3、urls
1 from django.conf.urls import url 2 from django.contrib import admin 3 from app01 import views 4 urlpatterns = [ 5 url(r'^admin/', admin.site.urls), 6 url(r'^login/$', views.login), 7 url(r'^index/$', views.index), 8 url(r'^userinfo/$', views.userinfo), 9 url(r'^userinfo/add/$', views.userinfo_add), 10 url(r'^userinfo/del/(\d+)/$', views.userinfo_del), 11 url(r'^userinfo/edit/(\d+)/$', views.userinfo_edit), 12 url(r'^order/$', views.order), 13 url(r'^order/add/$', views.order_add), 14 url(r'^order/del/(\d+)/$', views.order_del), 15 url(r'^order/edit/(\d+)/$', views.order_edit), 16 ]
4、初始化 server--init_permission
1 #!usr/bin/env python 2 # -*- coding:utf-8 -*- 3 from django.conf import settings 4 def init_permission(user, request): 5 ''' 6 初始化权限信息,获取权限信息并放置到session中 7 :param user: 8 :param request: 9 :return: 10 ''' 11 permission_list = user.roles.values('permissions__id', 12 'permissions__title', # 用户列表 13 'permissions__url', 14 'permissions__codes', 15 'permissions__menu_gp_id', # 组内菜单ID,Null表示是菜单 16 'permissions__group_id', 17 'permissions__group__menu_id', # 菜单ID 18 'permissions__group__menu__caption', # 菜单名称 19 ).distinct() # 获取当前角色对象的所有的权限并去重 20 # print(permission_list) 21 # 权限相关 22 url_dict = {} 23 for item in permission_list: 24 group_id = item["permissions__group_id"] 25 url = item["permissions__url"] 26 code = item["permissions__codes"] 27 # print("code_list", code) 28 if group_id in url_dict: 29 url_dict[group_id]["code"].append(code) # 如果id在里面就把code和url添加进去 30 url_dict[group_id]["urls"].append(url) 31 else: 32 # 如果不在就设置 33 url_dict[group_id] = { 34 "code": [code, ], 35 "urls": [url, ] 36 } 37 request.session[settings.PERMISSION_URL_DICT] = url_dict 38 print(url_dict) 39 40 41 #菜单相关 42 # 1、先去掉不是菜单的 43 menu_list = [] 44 for item in permission_list: 45 tpl = { 46 "id":item["permissions__id"], 47 "title":item["permissions__title"], 48 "url":item["permissions__url"], 49 "menu_gp_id":item["permissions__menu_gp_id"], 50 "menu_id":item["permissions__group__menu_id"], 51 "menu_title":item["permissions__group__menu__caption"] 52 } 53 menu_list.append(tpl) 54 request.session[settings.PERMISSION_MENU_KEY] = menu_list 55 print("============",menu_list)
5、app01下的views
views.py
1 from django.shortcuts import render, redirect, HttpResponse 2 from django.conf import settings 3 # Create your views here. 4 from rbac import models 5 import re 6 from rbac.service.init_permission import init_permission 7 class BasePagPermission(object): 8 def __init__(self, code_list): 9 self.code_list = code_list 10 11 def has_add(self): 12 if "add" in self.code_list: 13 return True 14 15 def has_del(self): 16 if "del" in self.code_list: 17 return True 18 19 def has_edit(self): 20 if "edit" in self.code_list: 21 return True 22 23 24 def login(request): 25 if request.method == "GET": 26 return render(request, "login.html") 27 else: 28 username = request.POST.get("username") 29 password = request.POST.get("password") 30 user = models.UserInfo.objects.filter(name=username, password=password).first() 31 if user: 32 init_permission(user, request) 33 return redirect("/userinfo/") 34 else: 35 return render(request, "login.html") 36 37 38 def index(request): 39 return render(request, "index.html") 40 41 42 def userinfo(request): 43 pagpermission = BasePagPermission(request.permission_code_url) # 实例化 44 # print("code......", request.permission_code_url) 45 data_list = [ 46 {"id": 1, "name": "haiyan1"}, 47 {"id": 2, "name": "haiyan2"}, 48 {"id": 3, "name": "haiyan3"}, 49 {"id": 4, "name": "haiyan4"}, 50 {"id": 5, "name": "haiyan5"}, 51 ] 52 #print("data_list",data_list) 53 #print("pagpermission",pagpermission) 54 return render(request, "userinfo.html", {"data_list": data_list, "pagpermission": pagpermission}) 55 56 57 def userinfo_add(request): 58 if request.method == "GET": 59 return render(request,"userinfo_add.html") 60 else: 61 return redirect("/userinfo/") 62 63 64 def userinfo_del(request, nid): 65 return HttpResponse("删除用户") 66 67 68 def userinfo_edit(request, nid): 69 return HttpResponse("编辑用户") 70 71 72 def order(request): 73 pagpermission = BasePagPermission(request.permission_code_url) # 实例化 74 print("code......", request.permission_code_url) 75 return render(request,"order.html",{"pagpermission":pagpermission}) 76 77 78 def order_add(request): 79 return HttpResponse("添加订单") 80 81 82 def order_del(request, nid): 83 return HttpResponse("删除订单") 84 85 86 def order_edit(request, nid): 87 return HttpResponse("编辑订单")
6、中间件
1 #!usr/bin/env python 2 # -*- coding:utf-8 -*- 3 from django.shortcuts import render, redirect, HttpResponse 4 from django.conf import settings 5 import re 6 7 8 class MiddlewareMixin(object): 9 def __init__(self, get_response=None): 10 self.get_response = get_response 11 super(MiddlewareMixin, self).__init__() 12 13 def __call__(self, request): 14 response = None 15 if hasattr(self, 'process_request'): 16 response = self.process_request(request) 17 if not response: 18 response = self.get_response(request) 19 if hasattr(self, 'process_response'): 20 response = self.process_response(request, response) 21 return response 22 23 24 class Middle(MiddlewareMixin): # 必须去继承这个类 25 def process_request(self, request): 26 current_url = request.path_info # 获取当前请求的路径 27 28 # 如果匹配的是白名单里面的就让通过,不需要权限 29 for url in settings.WHITE_LIST: 30 if re.match(url, current_url): 31 return None 32 permission_dict = request.session.get(settings.PERMISSION_URL_DICT) # 获取session 33 if not permission_dict: # 如果没有得到,就直接跳转到login页面 34 return redirect("/login/") 35 36 flag = False 37 for group_id, code_url in permission_dict.items(): 38 for db_url in code_url["urls"]: 39 regex = "^{0}$".format(db_url) 40 # 因为match匹配的时候会把你只要有的都匹配到了,我们只匹配当前的url,所以得加一个起始终止符 41 # print(regex, current_url) 42 if re.match(regex, current_url): 43 # print(1111111) 44 request.permission_code_url = code_url["code"] # 用户输入的url和数据库的url匹配成功之后成功之后先把code保存在request中,方便以后判断 45 flag = True 46 break # 如果匹配成功就进入页面 47 # 注意在这里不能用return,在中间件中process_request这个函数如果有return就只会执行自己的 48 # response和上面的response,不会执行后续的。没有return就会去继续执行后续中间件和视图函数 49 if flag: # 结束外层循环 50 break 51 if not flag: 52 return HttpResponse("无权访问") # 如果访问不成功就显示无权访问 53 54 def process_reponse(self, request, response): 55 return response
7、templatetags下的rbac.py
1 #!usr/bin/env python 2 # -*- coding:utf-8 -*- 3 from django.conf import settings 4 import re 5 from django.template import Library 6 register = Library() 7 @register.inclusion_tag("xxxx.html") 8 def menu_html(request): 9 """ 10 去Session中获取菜单相关信息,匹配当前URL,生成菜单 11 :param request: 12 :return: 13 """ 14 menu_list = request.session.get(settings.PERMISSION_MENU_KEY) 15 current_url = request.path_info 16 menu_dict = {} 17 for item in menu_list: 18 # 循环找到可以作为菜单的权限 19 if not item["menu_gp_id"]: 20 menu_dict[item["id"]] = item 21 # 正则匹配添加active 22 for item in menu_list: 23 regex = "^{0}$".format(item["url"]) 24 if re.match(regex, current_url): 25 # 匹配成功在根据id去判断,如果菜单id有值就不是菜单,则去找它的值对应的id,添加active 26 # ,为null时就是菜单,直接给自己添加一个active 27 if not item["menu_gp_id"]: #是菜单 28 menu_dict[item["id"]]["active"] = True 29 else: 30 menu_dict[item["menu_gp_id"]]["active"] = True 31 32 result = {} 33 for item in menu_dict.values(): 34 active = item.get("active") 35 menu_id = item["menu_id"] 36 if menu_id in result: 37 result[menu_id]["children"].append({'title': item['title'], 'url': item['url'], 'active': active}) 38 if active: 39 result[menu_id]["active"] = True 40 else: 41 result[menu_id] = { 42 'menu_id': item['menu_id'], 43 'menu_title': item['menu_title'], 44 'active': active, 45 'children': [ 46 {'title': item['title'], 'url': item['url'], 'active': active} 47 ] 48 } 49 print("result",result) 50 return {"menu_dict":result}
8、template
1 {% load rbac %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 7 <meta name="viewport" content="width=device-width"> 8 <title>Title</title> 9 <link rel="stylesheet" href="/static/rbac/rbac.css"> 10 </head> 11 <body> 12 <div class="aaa"> 13 <div class="left"> 14 {% menu_html request %} 15 </div> 16 <div class="right"> 17 {% block content %} 18 19 {% endblock %} 20 </div> 21 </div> 22 <script src="/static/jquery-3.2.1.min.js"></script> 23 <script src="/static/rbac/rbac.js"></script> 24 25 </body> 26 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width"> 7 <title>Title</title> 8 </head> 9 <body> 10 <h1>hello--{{ user.name }}</h1> 11 </body> 12 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width"> 7 <title>Title</title> 8 </head> 9 <body> 10 <form method="post" action="/login/"> 11 {% csrf_token %} 12 <p>姓名:<input type="text" name="username"></p> 13 <p>密码:<input type="password" name="password"></p> 14 <p><input type="submit" value="提交"></p> 15 </form> 16 </body> 17 </html>
1 {% extends "base.html" %} 2 {% block content %} 3 <h1>订单页面</h1> 4 {% endblock %}
1 {% extends "base.html" %} 2 {% block content %} 3 {% if pagpermission.has_add %} 4 <a href="/userinfo/add/">添加</a> 5 {% endif %} 6 <table border="1"> 7 <thead> 8 <th>编号</th> 9 <th>姓名</th> 10 <th>操作</th> 11 </thead> 12 <tbody> 13 {% for row in data_list %} 14 <tr> 15 <td>{{ row.id }}</td> 16 <td>{{ row.name }}</td> 17 <td> 18 {% if pagpermission.has_edit %} 19 <a href="#">编辑</a> 20 {% endif %} 21 {% if pagpermission.has_del %} 22 <a href="#">删除</a> 23 {% endif %} 24 </td> 25 </tr> 26 {% endfor %} 27 </tbody> 28 </table> 29 {% endblock %}
1 {% extends "base.html" %} 2 {% block content %} 3 <form action="" method="post"> 4 {% csrf_token %} 5 <p>编号:<input type="text" name="bianhao"></p> 6 <p>姓名:<input type="text" name="name"></p> 7 <input type="submit" value="提交"> 8 </form> 9 {% endblock %}
1 {% for k,item in menu_dict.items %} 2 <div class="item-title">{{ item.menu_title }}</div> 3 {% if item.active %} 4 <div class="item-permission"> 5 {% else %} 6 <div class="item-permission hide"> 7 {% endif %} 8 {% for v in item.children %} 9 {% if v.active %} 10 <a href="{{ v.url }}" class="active">{{ v.title }}</a> 11 {% else %} 12 <a href="{{ v.url }}">{{ v.title }}</a> 13 {% endif %} 14 {% endfor %} 15 </div> 16 {% endfor %} 17 18 {# <div class="item-title">菜单一</div>#} 19 {# <div class="item-permission">#} 20 {# <a href="">用户列表</a>#} 21 {# <a href="">订单列表</a>#} 22 {# </div>#} 23 {# <div class="item-title">菜单二</div>#} 24 {# <div class="item-permission hide">#} 25 {# <a href="">用户列表</a>#} 26 {# <a href="">订单列表</a>#} 27 {# </div>#}
9、static---rbac---rbac.css
1 .left{ 2 float: left; 3 width: 20%; 4 height: 500px; 5 background-color: coral; 6 padding: 20px 20px; 7 } 8 .right{ 9 width: 80%; 10 height: 500px; 11 background-color: azure; 12 } 13 .item-permission a{ 14 display: block; 15 } 16 .hide{ 17 display: none; 18 } 19 .item-permission a.active{ 20 color: red; 21 }
10、static---rbac---rbac.js
1 // 方式一 2 $(function () { 3 $(".item-title").click(function () { 4 $(this).next().toggleClass("hide") 5 }) 6 }); 7 // 方式二: 8 // $(function () { 9 // $('.item-title').click(function () { 10 // if($(this).next().hasClass('hide')){ 11 // $(this).next().removeClass('hide') 12 // }else{ 13 // $(this).next().addClass('hide') 14 // } 15 // }) 16 // });