Django--权限组件 创建组件
需求分析:
创建独立app, rbac
##注意:
app创建后需要注册到setting.py中
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rbac.apps.RbacConfig', 'app01.apps.App01Config', ]
设计权限表
方案1:
创建用户表和创建权限表,然后两个表通过一个用户可能有多各权限 然后一个权限可以属于多个用户,所以两张表创建多对多关系,但是存在很多用户有相同的权限字段,所以参考方案二。
方案2:
创建用户表、创建权限表和角色表,为每个角色添加权限,然后用户通过角色获取自己的权限,分析如下所示
用户表: ID username password .... 1 番禺 123 2 夹缝 123 3 果冻 123 4 鲁宁 123 角色表: ID title 1 CEO 2 CTO 4 COO 5 部门经理 6 技术员 用户和角色关系表: 用户ID 角色ID 1 1 1 2 1 4 2 5 3 6 4 6 权限表: ID url title 1 /index/ 首页 2 /userinfo/ 用户列表 3 /userinfo/add/ 添加用户 4 /userinfo/del/(d+)/ 删除用户 5 /userinfo/edit/(d+)/ 修改用户 角色权限关系表: 角色ID 权限ID 1 1
具体表设计如下所示:
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=64) is_menu = models.BooleanField(verbose_name="是否是菜单") class Meta: verbose_name_plural = "权限表" def __str__(self): return self.title class User(models.Model): """ 用户表 """ username = 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) class Meta: verbose_name_plural = "用户表" def __str__(self): return self.username class Role(models.Model): """ 角色表 """ title = models.CharField(max_length=32) permissions = models.ManyToManyField(verbose_name='具有的所有权限',to='Permission',blank=True) class Meta: verbose_name_plural = "角色表" def __str__(self): return self.title
然后通过管理类同步数据库,添加数据。
3. 权限录入: CEO:番禺 /userinfo/ /userinfo/add/ /userinfo/edit/(d+)/ /userinfo/del/(d+)/ /order/ /order/add/ /order/edit/(d+)/ /order/del/(d+)/ 总监:鲁宁 /userinfo/ /userinfo/add/ /order/ /order/add/ 经理:肾松 /userinfo/ /order/ 业务员:肾松,文飞,成栋 /order/
配置用户调用接口
1. 本地化用户权限
在rbac中创建一个初始化文件,用于本地化权限信息,使调用者方便调用
from django.conf import settings def init_permission(request,user): """ 本地化用户权限""" # 获取当前用户所有权限(重) permission_list = user.roles.values('permissions__url').distinct() request.session[settings.PERMISSION_LIST] = [url['permissions__url'] for url in permission_list]
2. 通过中间键进行用户请求url过滤
settings.py中
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'rbac.middleware.PermissionMiddleware', # 将添加的中间件配置到settings中
]
#####################confirm################### SESSION_KEY = 'auth_user' PERMISSION_LIST = 'permission_list' INVALIDATION_LIST = [ '/admin.*', '/login/', '/register/', ]
创建middleware.py
# encoding:utf-8 # Author:"richie" # Date:11/7/2017 import re from django.conf import settings from django.shortcuts import HttpResponse,redirect class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response class PermissionMiddleware(MiddlewareMixin): def process_request(self, request): current_path = request.path_info for url in settings.INVALIDATION_LIST: if re.match(url,current_path): return None permission_list = request.session.get(settings.PERMISSION_LIST) if not permission_list: return redirect('/login/') flag = False for url in permission_list: regular = "^{0}$".format(url) # 因为添加url的时候没有添加严格匹配的规则,所以在这里转格式 if re.match(regular,current_path): flag = True break if not flag: # 没有匹配上 return HttpResponse('无权访问')
权限组件使用
分配url,
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^login/', views.login, name='login'), url(r'^index/', views.home, name='home'), url(r'^userinfo/', views.home, name='home'), url(r'^userinfo/add', views.home, name='home'), url(r'^order/', views.home, name='home'), url(r'^order/add', views.home, name='home'), ]
定义视图函数:
from django.shortcuts import render,redirect from django.conf import settings from rbac.models import User from rbac.service import init_permission # Create your views here. class UserLoginForm(forms.ModelForm): """ 定义用户登录的from表单 """ class Meta: model = models.User fields = ['username','password'] def login(request): if request.method == 'POST': # 获取用户from表单数据 login_form = forms.UserLoginForm(request.POST) # 数据格式验证 if login_form.is_valid(): #用户认证 username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password') # 用户数据数据库验证 user =User.objects.filter(username=username,password=password).first() if user: #初始化权限系统 init_permission(request,user) # 添加用户信息 request.session[settings.SESSION_KEY] = {'uid':user.id,'username':username} return redirect('/home/') else: # 登录失败 添加错误信息 login_form.errors.update({'user_error': '用户名或密码错误'}) else: login_form = forms.UserLoginForm() return render(request, 'login.html',{'form':login_form}) def home(request): """ 主页面 :param request: :return: """ #获取用户名信息 username = request.session.get(settings.SESSION_KEY).get('username') return render(request,'home.html',locals())
3. 定义模板页面
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <form method="post" novalidate>{% csrf_token %} {{ form.errors.user_error }} {% for filed in form %} <p>{{ filed.label_tag }}{{ filed }}</p> <p>{{ filed.errors.as_text }}</p> {% endfor %} <input type="submit" value="提交"> </form> </body> </html>
定义home.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> {{ username }} </body> </html>