一, 引入
1.为什么要有权限?
2.为什么要开发权限的组件?
3.在web开发中,什么是权限?
4.表结构的设计
权限表
ID
URL
1
/user_list/
2
/customer_list/
用户表
ID
USER_NAME
1
root
2
root 2
角色/用户组表
ID
组
1
销售
2
开发
用户与角色的关系表
ID
USER_ID
角色ID
1
1
1
2
1
2
3
2
1
4
2
2
角色与权限的关系表
ID
角色ID
权限ID
1
1
1
2
1
2
3
2
1
4
2
2
models:
from django.db import models
# 权限表
class Permission(models.Model):
url = models.CharField(max_length=108, verbose_name='权限')
# 角色表
class Role(models.Model):
name = models.CharField(max_length=108, verbose_name='角色')
permissions = models.ManyToManyField('Permission', verbose_name='角色所拥有的权限', related_name='roles')
# 用户表
class UserInfo(models.Model):
username = models.CharField(max_length=32, verbose_name='用户名')
password = models.CharField(max_length=32, verbose_name='密码')
roles = models.ManyToManyField('Role', verbose_name='用户所拥有的角色', related_name='users')
基本流程:
二, admin的展示
from django.contrib import admin
from rbac import models
# 配置类
class PermissionAdmin(admin.ModelAdmin):
# 展示
list_display = ['title', 'url']
# 可编辑
list_editable = ['url']
admin.site.register(models.Role)
admin.site.register(models.Permission, admin_class=PermissionAdmin)
admin.site.register(models.UserInfo)
三, 记录登录状态与权限
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect, reverse
from django.conf import settings
import re
def login(request):
error = ''
if request.method == 'POST':
# 获取用户名和密码
username = request.POST.get('username')
password = request.POST.get('password')
# 数据库校验用户名密码是否正确
user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
if not user_obj:
# 校验失败,返回登录页面
error = '用户名或密码错误'
else:
# 登录成功
# 查询当前用户的权限信息
# permission = models.Permission.objects.filter(roles__users=user_obj).values('url').distinct()
permission = user_obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url').distinct()
# 保存权限信息
request.session['permission'] = list(permission)
request.session['is_login'] = 1
# 重定向去首页
return redirect('index')
return render(request, 'login.html', {'error': error})
四, 中间件校验权限
class Rbac(MiddlewareMixin):
def process_request(self, request):
# 获取当前访问的地址
url = request.path_info
# 白名单
for i in settings.WHITE_LIST:
if re.match(i, url):
return
# 登陆状态的校验
is_login = request.session.get('is_login')
if not is_login:
return redirect('login]')
# 免认证校验
for i in settings.PASS_AUTH_LIST:
if re.match(i, url):
return
# 获取权限信息
permissions = request.session.get('permission')
# 权限的校验
for i in permissions:
if re.match(r'^{}$'.format(i['permissions__url']), url):
return
# 拒绝请求
return HttpResponse('没有访问权限,请联系管理员')
# settings.py中
# rbac 白名单
WHITE_LIST = [
r'^/admin/',
r'^/login/$',
r'^/register/$',
]
# rabc 免认证
PASS_AUTH_LIST =[
r'^/index/$'
]
五, 动态生成一级菜单
首先规范化RBAC:
在settings.py中配置
PERMISSION_SESSION_KEY session中记录权限的key
MENU_SESSION_KEY session中记录菜单信息的key
LOGIN_SESSION_KEY session中记录登录状态的key
将相关template,static,服务等整理到rbac(app)内
model的更改:
# 权限表
class Permission(models.Model):
url = models.CharField(max_length=108, verbose_name='权限')
title = models.CharField(max_length=108, verbose_name='标题')
is_menu = models.BooleanField(default=False, choices=((True, '是'), (False, '否')), verbose_name='是否目录')
icon = models.CharField(max_length=64, verbose_name='图标', null=True, blank=True)
def __str__(self):
return self.title
session记录的更改:
# 查询当前用户的权限信息
# permission = models.Permission.objects.filter(roles__users=user_obj).values('url').distinct()
permission = user_obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
'permissions__title',
'permissions__is_menu',
'permissions__icon',
).distinct()
# 权限的列表
permission_list = []
# 菜单的列表
menu_list = []
for i in permission:
permission_list.append({
'url': i['permissions__url']
})
if i.get('permissions__is_menu'):
menu_list.append({
'title': i['permissions__title'],
'icon': i['permissions__icon'],
'url': i['permissions__url']
})
# 保存权限信息
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
request.session[settings.LOGIN_SESSION_KEY] = True
# 保存菜单信息
request.session[settings.MENU_SESSION_KEY] = menu_list
自定义inclusion_tag:
// 插件 menu.html
from django import template
from django.conf import settings
register = template.Library()
@register.inclusion_tag('rbac/menu.html')
def menu(request):
menu_list = request.session.get(settings.MENU_SESSION_KEY)
url = request.path_info
for menu in menu_list:
if menu.get('url') == url:
menu['class'] = 'active'
return {'menu_list': request.session.get(settings.MENU_SESSION_KEY)}
六, 动态生成二级菜单
model的更改:
# 一级菜单表
class Menu(models.Model):
title = models.CharField(max_length=108, verbose_name='一级菜单标题')
icon = models.CharFie