django rest framwork用户权限设计和Authentication用户认证?

为什么要在业务中实现用户权限管理?

        在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

图片

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值