在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
相关产品推荐
相关产品推荐

