为什么要在业务中实现用户权限管理?
在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。