赞
踩
为什么要在业务中实现用户权限管理?
在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。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。