You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Django中实现类AWS IAM的RBAC体系的技术问题咨询

在Django中实现类AWS IAM的RBAC体系的技术问题咨询

我来帮你梳理下实现思路,其实你现在的核心痛点是缺少AWS IAM里的「账号」顶层概念——AWS的所有用户、资源都是绑定在同一个账号下的,而你目前直接用created_by绑定用户和资源,就会出现普通用户看不到root资源的问题。下面是一步步的解决方案:

一、先补全「账号」核心实体

首先要创建一个Account模型,用来模拟AWS的账号,把root用户、下属用户、所有业务资源都绑定到这个账号下:

from django.contrib.auth.models import User
from django.db import models

# 模拟AWS账号的核心模型
class Account(models.Model):
    root_user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="owned_account")
    account_name = models.CharField(max_length=100, verbose_name="账号名称")
    created_at = models.DateTimeField(auto_now_add=True)

# 给用户扩展账号关联字段(推荐用这种方式,不要直接修改原生User)
class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bound_account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="account_users")
    phone = models.CharField(max_length=20, null=True, blank=True)  # 用于手机号登录

# 修改你的业务模型,把资源归属从created_by改成绑定账号
class MyModel(models.Model):
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name="创建人")
    bound_account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="model_resources")
    # 其他业务字段...

这样调整后,你的视图查询逻辑就要从created_by=request.user改成按账号过滤:

# 视图中的查询集修改为
MyModel.objects.filter(bound_account=request.user.userprofile.bound_account)

不管是root还是普通用户,只要在同一个账号下,就能看到账号内的所有资源,完美解决你说的「普通用户看不到root创建的对象」问题。

二、优化组权限的归属逻辑

你之前给Group加了created_by,但现在结合账号体系,应该让组归属于账号而不是单个用户:

from django.contrib.auth.models import Group

class CustomGroup(Group):
    bound_account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="account_groups")
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name="创建人")

这样不管是root还是普通用户创建的组,都属于所在的账号,root可以统一管理账号下的所有组,不会出现「组归属脱离root」的问题。

三、权限检查的最佳实践

关于权限检查的位置,分两种场景来看:

  • 视图层检查:这是最推荐的方式,适合大部分细粒度的业务权限。可以用Django自带的PermissionRequiredMixin或者自定义装饰器,比如:
    from django.contrib.auth.decorators import permission_required
    from django.utils.decorators import method_decorator
    from django.views.generic import ListView
    
    @method_decorator(permission_required('myapp.view_mymodel'), name='dispatch')
    class MyModelListView(ListView):
        def get_queryset(self):
            return MyModel.objects.filter(bound_account=self.request.user.userprofile.bound_account)
    
  • 中间件层检查:只适合全局通用规则(比如所有请求必须属于某个有效账号),不适合细粒度的模型/操作权限——因为中间件能拿到的请求信息有限,很难判断具体要访问的资源和操作类型。

另外,你提到未来可能需要对象级权限,到时候可以基于「账号+用户/组」的维度来扩展,比如给特定组开放账号内某几个对象的编辑权限,逻辑上完全兼容当前的架构。

四、Root用户的特殊权限处理

AWS的root用户拥有所有权限,你可以在权限检查逻辑里加一个特殊判断:

def check_user_permission(request, perm):
    # 如果是当前账号的root用户,直接跳过所有权限校验
    if request.user == request.user.userprofile.bound_account.root_user:
        return True
    # 普通用户走正常权限校验
    return request.user.has_perm(perm)

完全不需要把root加入任何组,它本身就是账号的所有者,天然拥有最高权限。

五、邮箱/手机号登录的实现

Django默认用username登录,你可以自定义认证后端来支持邮箱/手机号:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User

class EmailOrPhoneBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            # 先尝试匹配邮箱
            user = User.objects.get(email=username)
        except User.DoesNotExist:
            # 再尝试匹配手机号(关联UserProfile的phone字段)
            try:
                user = User.objects.get(userprofile__phone=username)
            except User.DoesNotExist:
                return None
        # 校验密码
        if user.check_password(password):
            return user

然后在settings.py里配置这个后端:

AUTHENTICATION_BACKENDS = [
    'myapp.backends.EmailOrPhoneBackend',
    'django.contrib.auth.backends.ModelBackend',
]

备注:内容来源于stack exchange,提问作者Pranta chakraborty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 14:20:30