为什么要在业务中实现用户权限管理?
在B/S系统中,浏览器是每一台计算机都已具备的,如果不建立一个完整的权限检测,那么一个"非法用户"很可能通过浏览器轻易访问到B/S系统中的所有功能。因此B/S系统业务中都需要一个或多个权限系统来实现访问权限检测,让经过授权的用户可以正常合法的使用已授权功能,而对那些未授权的“非法用户”会将他们彻底的“拒之门外”。
01 需求陈述
-
不同职责的人,对于系统 *** 作权限是不同的。
-
根据“组”进行权限分配,将权限一致的人员编入同一组,然后对改组进行权限分配。
-
权限管理系统应该是可扩展的。它应该可以加入到任何带有权限管理的系统中。就像组件一样可以不断的重用,而不是每开发一套管理系统,就要针对权限管理部分进行重新开发。
-
基于django rest_framework设计登录身份token认证、权限功能设计和实现等。
在navicat中ER图标中,权限表、角色表、用户表之间是多不多的关系。
02 用户权限表设计
from django.db import models from django.contrib.auth.models import AbstractUser class Position(models.Model): """ 职位/岗位 """ name = models.CharField('名称', max_length=32, unique=True) description = models.CharField('描述', max_length=50, blank=True, null=True) class meta: verbose_name = '职位/岗位' verbose_name_plural = verbose_name def __str__(self): return self.name class Organization(models.Model): """ 组织架构 """ organization_type_choices = ( ('公司', '公司'), ('部门', '部门') ) name = models.CharField('名称', max_length=60) type = models.CharField('类型', max_length=20, choices=organization_type_choices, default='部门') parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='父') class meta: verbose_name = '组织架构' verbose_name_plural = verbose_name def __str__(self): return self.name class Permission(models.Model): """ 功能权限:目录,菜单,接口 """ menu_type_choices = ( ('目录', '目录'), ('菜单', '菜单'), ('接口', '接口') ) name = models.CharField('名称', max_length=30) type = models.CharField('类型', max_length=20, choices=menu_type_choices, default='接口') is_frame = models.BooleanField('外部链接', default=False) sort = models.IntegerField('排序标记', default=1) parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='父') method = models.CharField('方法/代号', max_length=50,unique=True, null=True, blank=True) def __str__(self): return self.name class meta: verbose_name = '功能权限表' verbose_name_plural = verbose_name ordering = ['sort'] # 用户角色表 class Role(models.Model): name = models.CharField('角色', max_length=32, unique=True) perms = models.ManyToManyField(Permission, blank=True, verbose_name='功能权限') depts = models.ManyToManyField(Organization, blank=True, verbose_name='权限范围') description = models.CharField('描述', max_length=50, blank=True, null=True) class meta: verbose_name = '角色' verbose_name_plural = verbose_name def __str__(self): return self.name # 用户表 class User(AbstractUser): name = models.CharField('姓名', max_length=20, null=True, blank=True) phone = models.CharField('手机号', max_length=11, null=True, blank=True, unique=True) avatar = models.CharField('头像', default='/media/default/avatar.png', max_length=100, null=True, blank=True) superior = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管') roles = models.ManyToManyField(Role, blank=True, verbose_name='角色') position = models.ManyToManyField(Position, blank=True, verbose_name='岗位') dept = models.ForeignKey( Organization, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='组织') class meta: verbose_name = '用户信息' verbose_name_plural = verbose_name ordering = ['id'] def __str__(self): return self.username
03 修改配置和权限代码
修改settings配置
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'apps.system' ] # JWT设置 SIMPLE_JWT = { 'REFRESH_TOKEN_LIFETIME': timedelta(minutes=1), 'ACCESS_TOKEN_LIFETIME': timedelta(days=10), # 配置token过期时间 'ROTATE_REFRESH_TOKENS': True, } # 自定义认证后台(Backend) AUTH_USER_MODEL = 'system.User' AUTHENTICATION_BACKENDS = ( 'utils.authentication.CustomBackend', ) REST_frameWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', ], 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', ], }
用户身份认证authentication.py
from django.contrib.auth.backends import ModelBackend from django.db.models import Q from django.contrib.auth import get_user_model UserModel = get_user_model() class CustomBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): if username is None: username = kwargs.get(UserModel.USERNAME_FIELD) if username is None or password is None: return try: user = UserModel._default_manager.get( Q(username=username) | Q(phone=username) | Q(email=username)) except UserModel.DoesNotExist: # Run the default password hasher once to reduce the timing # difference between an existing and a nonexistent user (#20760). UserModel().set_password(password) else: if user.check_password(password) and self.user_can_authenticate(user): return user
自定义权限验证permission.py
from django.core.cache import cache from rest_framework.permissions import basePermission from apps.system.models import Permission def get_permission_list(user): """ 获取权限列表,可用redis存取 """ if user.is_superuser: perms_list = ['admin'] else: perms = Permission.objects.none() roles = user.roles.all() if roles: for i in roles: perms = perms | i.perms.all() perms_list = perms.values_list('method', flat=True) perms_list = list(set(perms_list)) cache.set(user.username + '__perms', perms_list, 60 * 60) return perms_list class RbacPermission(basePermission): """ 基于角色的权限校验类 """ def has_permission(self, request, view): """ 权限校验逻辑 :param request: :param view: 视图对象 :return: """ path = request._request.path # client访问的api接口名 perms = get_permission_list(request.user) # list if'admin'in perms: return True if perms: if path in perms: return True else: return False else: return False def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ ifnot request.user: return False return True
单个视图权限控制views.py
from .models import Category from utils.permission import RbacPermission class CategoryViewSet(ModelViewSet): permission_classes = [RbacPermission] queryset = Category.objects.all() serializer_class = CategoryListSerializer search_fields = ['name']
全局权限设置
REST_frameWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', 'apps.system.permission.RbacPermission' ], 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', ], }
登录token获取
from django.contrib import admin from django.urls import path, include from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, ) from rest_framework import routers from apps.system.views import LogoutView router = routers.DefaultRouter() urlpatterns = [ path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('token/black/', LogoutView.as_view(), name='token_black'), path('system/', include('apps.system.urls')), path('blog/', include('apps.blog.urls')), ]
postman测试登录接口
用户管理列表
用户张三的权限列表
用户的权限分配
权限菜单配置
权限系统可以说是整个系统中最基础,同时也可以很复杂的,在实际项目中,会遇到多个系统,多个用户类型,多个使用场景,这就需要具体问题具体分析,但最核心的RBAC模型是不变的,我们可以在其基础上进行扩展来满足需求。最后,如果您觉得这篇文章对您有帮助,可以点个赞,谢谢支持!
参考:
https://github.com/caoqianming/django-vue-admin
希望对大家有所帮助有问题可以联系作者。感兴趣的可以关注作者微信公众号:程序员9527。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)