找回密码
 立即注册
首页 业界区 安全 Django用户认证权限

Django用户认证权限

缣移双 昨天 14:18
Django 提供了一个强大且灵活的认证和权限系统,可以轻松处理用户认证、授权和权限管理。
1、认证系统架构

启用配置
  1. # settings.py
  2. INSTALLED_APPS = [
  3.     # ...
  4.     'django.contrib.auth',  # 核心认证框架
  5.     'django.contrib.contenttypes',  # 权限系统依赖
  6.     # ...
  7. ]
  8. MIDDLEWARE = [
  9.     # ...
  10.     'django.contrib.auth.middleware.AuthenticationMiddleware',  # 关联用户与请求
  11.     # ...
  12. ]
复制代码
1.1 User 模型

存储用户信息的核心模型
1.1.1 模型源码
  1. class PermissionsMixin(models.Model):
  2.     # 是否为超级用户
  3.     is_superuser = models.BooleanField(
  4.         _("superuser status"),
  5.         default=False,
  6.         help_text=_(
  7.             "Designates that this user has all permissions without "
  8.             "explicitly assigning them."
  9.         ),
  10.     )
  11.     groups = models.ManyToManyField(
  12.         Group,
  13.         verbose_name=_("groups"),
  14.         blank=True,
  15.         help_text=_(
  16.             "The groups this user belongs to. A user will get all permissions "
  17.             "granted to each of their groups."
  18.         ),
  19.         related_name="user_set",
  20.         related_query_name="user",
  21.     )
  22.     user_permissions = models.ManyToManyField(
  23.         Permission,
  24.         verbose_name=_("user permissions"),
  25.         blank=True,
  26.         help_text=_("Specific permissions for this user."),
  27.         related_name="user_set",
  28.         related_query_name="user",
  29.     )
  30.     class Meta:
  31.         abstract = True
  32. class AbstractBaseUser(models.Model):
  33.     # 哈希后的密码
  34.     password = models.CharField(_("password"), max_length=128)
  35.     last_login = models.DateTimeField(_("last login"), blank=True, null=True)
  36.     is_active = True
  37.     REQUIRED_FIELDS = []
  38.     _password = None
  39.     class Meta:
  40.         abstract = True
  41.     def __str__(self):
  42.         return self.get_username()
  43. class AbstractUser(AbstractBaseUser, PermissionsMixin):
  44.     """
  45.     抽象用户模型,提供了完整的用户功能
  46.     """
  47.     username_validator = UnicodeUsernameValidator()
  48.     # 用户名(唯一标识)
  49.     username = models.CharField(
  50.         _("username"),
  51.         max_length=150,
  52.         unique=True,
  53.         help_text=_(
  54.             "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
  55.         ),
  56.         validators=[username_validator],
  57.         error_messages={
  58.             "unique": _("A user with that username already exists."),
  59.         },
  60.     )
  61.     first_name = models.CharField(_("first name"), max_length=150, blank=True)
  62.     last_name = models.CharField(_("last name"), max_length=150, blank=True)
  63.     email = models.EmailField(_("email address"), blank=True)
  64.     # 是否可以登录管理后台
  65.     is_staff = models.BooleanField(
  66.         _("staff status"),
  67.         default=False,
  68.         help_text=_("Designates whether the user can log into this admin site."),
  69.     )
  70.     # 账号是否激活
  71.     is_active = models.BooleanField(
  72.         _("active"),
  73.         default=True,
  74.         help_text=_(
  75.             "Designates whether this user should be treated as active. "
  76.             "Unselect this instead of deleting accounts."
  77.         ),
  78.     )
  79.     date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
  80.     objects = UserManager()
  81.     EMAIL_FIELD = "email"
  82.     USERNAME_FIELD = "username"
  83.     REQUIRED_FIELDS = ["email"]
  84.     class Meta:
  85.         verbose_name = _("user")
  86.         verbose_name_plural = _("users")
  87.         abstract = True
  88.     def clean(self):
  89.         super().clean()
  90.         self.email = self.__class__.objects.normalize_email(self.email)
  91. class User(AbstractUser):
  92.     # 默认用户模型,继承自 AbstractUser
  93.     class Meta(AbstractUser.Meta):
  94.         swappable = "AUTH_USER_MODEL"
复制代码
1.1.2 自定义用户模型

最佳实践是项目开始时创建自定义用户模型
  1. # models.py
  2. from django.contrib.auth.models import AbstractUser
  3. from django.db import models
  4. class CustomUser(AbstractUser):
  5.     # 添加额外字段
  6.     phone_number = models.CharField(max_length=15, blank=True)
  7.     date_of_birth = models.DateField(null=True, blank=True)
  8.    
  9.     # 使用邮箱作为用户名
  10.     REQUIRED_FIELDS = ['username']  # 创建超级用户时需要的字段
复制代码
在 settings.py 中指定:
  1. AUTH_USER_MODEL = 'myapp.CustomUser'
复制代码
1.2 权限模型

1.2.1 模型源码
  1. class Permission(models.Model):
  2.     """
  3.     权限模型,存储所有权限信息
  4.     """
  5.     name = models.CharField(_("name"), max_length=255)
  6.     content_type = models.ForeignKey(
  7.         ContentType,
  8.         models.CASCADE,
  9.         verbose_name=_("content type"),
  10.     )
  11.     codename = models.CharField(_("codename"), max_length=100)
  12.     objects = PermissionManager()
  13.     class Meta:
  14.         verbose_name = _("permission")
  15.         verbose_name_plural = _("permissions")
  16.         unique_together = [["content_type", "codename"]]
  17.         ordering = ["content_type__app_label", "content_type__model", "codename"]
  18.     def __str__(self):
  19.         return "%s | %s" % (self.content_type, self.name)
  20.     def natural_key(self):
  21.         return (self.codename,) + self.content_type.natural_key()
  22.     natural_key.dependencies = ["contenttypes.contenttype"]
复制代码
Django 自动为每个模型创建四个基本权限,在模型迁移时自动创建

  • add_ - 添加权限
  • change_ - 修改权限
  • delete_ - 删除权限
  • view_ - 查看权限
1.2.2 自定义权限

可以在模型的 Meta 类中定义自定义权限:
  1. class Book(models.Model):
  2.     title = models.CharField(max_length=100)
  3.     author = models.CharField(max_length=100)
  4.    
  5.     class Meta:
  6.         permissions = [
  7.             ("can_publish", "Can publish book"),
  8.             ("can_edit_cover", "Can edit book cover"),
  9.         ]
复制代码
1.2.2 权限检查
  1. # django\contrib\auth\models.py
  2. def _user_has_perm(user, perm, obj):
  3.     """
  4.     检查用户是否有特定权限
  5.     """
  6.     for backend in auth.get_backends():
  7.         if not hasattr(backend, "has_perm"):
  8.             continue
  9.         try:
  10.             if backend.has_perm(user, perm, obj):
  11.                 return True
  12.         except PermissionDenied:
  13.             return False
  14.     return False
复制代码
1.3 组

用户组,用于批量权限分配
1.3.1 模型源码
  1. class Group(models.Model):
  2.     """
  3.     组模型,用于批量分配权限
  4.     """
  5.     name = models.CharField(_("name"), max_length=150, unique=True)
  6.     permissions = models.ManyToManyField(
  7.         Permission,
  8.         verbose_name=_("permissions"),
  9.         blank=True,
  10.     )
  11.     objects = GroupManager()
  12.     class Meta:
  13.         verbose_name = _("group")
  14.         verbose_name_plural = _("groups")
  15.     def __str__(self):
  16.         return self.name
  17.     def natural_key(self):
  18.         return (self.name,)
复制代码
1.4 认证后端

Django 认证系统支持多个认证后端,按顺序尝试认证,authenticate()函数会遍历 AUTHENTICATION_BACKENDS设置中指定的所有后端。默认使用的是 ModelBackend,其核心逻辑是根据 username查询用户,并验证其密码和 is_active状态
  1. # settings.py
  2. AUTHENTICATION_BACKENDS = [
  3.     'django.contrib.auth.backends.ModelBackend',  # 默认后端
  4.     'myapp.backends.EmailBackend',  # 自定义后端
  5. ]
复制代码
1.4.1 ModelBackend源码
  1. class ModelBackend(BaseBackend):
  2.     """
  3.     基于模型的认证后端
  4.     """
  5.     def authenticate(self, request, username=None, password=None, **kwargs):
  6.         if username is None:
  7.             username = kwargs.get(UserModel.USERNAME_FIELD)
  8.         if username is None or password is None:
  9.             return
  10.         try:
  11.             user = UserModel._default_manager.get_by_natural_key(username)
  12.         except UserModel.DoesNotExist:
  13.             # 模拟密码哈希以防止时序攻击
  14.             UserModel().set_password(password)
  15.         else:
  16.             if user.check_password(password) and self.user_can_authenticate(user):
  17.                 return user
  18.     def user_can_authenticate(self, user):
  19.         """
  20.         检查用户是否可以认证(是否激活)
  21.         """
  22.         return getattr(user, "is_active", True)
  23.     def _get_user_permissions(self, user_obj):
  24.         # 获取用户权限的实现
  25.         return user_obj.user_permissions.all()
  26.     def _get_group_permissions(self, user_obj):
  27.         return Permission.objects.filter(group__in=user_obj.groups.all())
  28.     def _get_permissions(self, user_obj, obj, from_name):
  29.         # 获取权限
  30.         if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
  31.             return set()
  32.         perm_cache_name = "_%s_perm_cache" % from_name
  33.         if not hasattr(user_obj, perm_cache_name):
  34.             if user_obj.is_superuser:
  35.                 perms = Permission.objects.all()
  36.             else:
  37.                 perms = getattr(self, "_get_%s_permissions" % from_name)(user_obj)
  38.             perms = perms.values_list("content_type__app_label", "codename").order_by()
  39.             setattr(
  40.                 user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms}
  41.             )
  42.         return getattr(user_obj, perm_cache_name)
  43.     def get_user_permissions(self, user_obj, obj=None):
  44.         # 获取用户权限
  45.         return self._get_permissions(user_obj, obj, "user")
  46.    
  47.     def get_group_permissions(self, user_obj, obj=None):
  48.         # 获取组权限
  49.         return self._get_permissions(user_obj, obj, "group")
  50.     def get_all_permissions(self, user_obj, obj=None):
  51.         # 获取所有权限
  52.         if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
  53.             return set()
  54.         if not hasattr(user_obj, "_perm_cache"):
  55.             user_obj._perm_cache = super().get_all_permissions(user_obj)
  56.         return user_obj._perm_cache
  57.     def has_perm(self, user_obj, perm, obj=None):
  58.         # 检查用户是否有特定权限
  59.         return user_obj.is_active and super().has_perm(user_obj, perm, obj=obj)
  60.     def get_user(self, user_id):
  61.         try:
  62.             user = UserModel._default_manager.get(pk=user_id)
  63.         except UserModel.DoesNotExist:
  64.             return None
  65.         return user if self.user_can_authenticate(user) else None
复制代码
1.4.2 自定义认证后端
  1. # backends.py
  2. from django.contrib.auth.backends import BaseBackend
  3. from django.contrib.auth import get_user_model
  4. from django.db.models import Q
  5. class EmailBackend(BaseBackend):
  6.     """
  7.     使用邮箱认证的后端
  8.     """
  9.     def authenticate(self, request, username=None, password=None, **kwargs):
  10.         UserModel = get_user_model()
  11.         try:
  12.             # 使用邮箱或用户名认证
  13.             user = UserModel.objects.get(
  14.                 Q(username__iexact=username) | Q(email__iexact=username)
  15.             )
  16.         except UserModel.DoesNotExist:
  17.             # 运行默认密码哈希器以防计时攻击
  18.             UserModel().set_password(password)
  19.             return None
  20.         except UserModel.MultipleObjectsReturned:
  21.             # 如果找到多个用户,返回第一个
  22.             user = UserModel.objects.filter(
  23.                 Q(username__iexact=username) | Q(email__iexact=username)
  24.             ).first()
  25.         
  26.         if user and user.check_password(password):
  27.             return user
  28.         return None
  29.    
  30.     def get_user(self, user_id):
  31.         UserModel = get_user_model()
  32.         try:
  33.             return UserModel.objects.get(pk=user_id)
  34.         except UserModel.DoesNotExist:
  35.             return None
复制代码
1.4.3 authenticate和login方法
  1. @sensitive_variables("credentials")
  2. def authenticate(request=None, **credentials):
  3.     # 遍历AUTHENTICATION_BACKENDS设置中指定的所有后端
  4.     for backend, backend_path in _get_compatible_backends(request, **credentials):
  5.         try:
  6.             # 调用认证后端的authenticate核心方法
  7.             user = backend.authenticate(request, **credentials)
  8.         except PermissionDenied:
  9.             break
  10.         if user is None:
  11.             continue
  12.         user.backend = backend_path
  13.         return user
  14.     # 发送认证失败信号
  15.     user_login_failed.send(
  16.         sender=__name__, credentials=_clean_credentials(credentials), request=request
  17.     )
  18. def login(request, user, backend=None):
  19.     # 主要是session的处理,保存是在session中间件完成
  20.     # ...
复制代码
2、基本使用

2.1 用户认证

authenticatelogin函数处理
  1. from django.contrib.auth import authenticate, login
  2. from django.shortcuts import render, redirect
  3. from django.contrib.auth.forms import UserCreationForm
  4. def login_view(request):
  5.     if request.method == 'POST':
  6.         username = request.POST['username']
  7.         password = request.POST['password']
  8.         # 认证
  9.         user = authenticate(request, username=username, password=password)
  10.         
  11.         if user is not None:
  12.             # 登录(建立会话)
  13.             login(request, user)
  14.             return redirect('home')
  15.         else:
  16.             return render(request, 'login.html', {'error': 'Invalid credentials'})
  17.    
  18.     return render(request, 'login.html')
  19. def register_view(request):
  20.     if request.method == 'POST':
  21.         form = UserCreationForm(request.POST)
  22.         if form.is_valid():
  23.             user = form.save()
  24.             # 创建完成后登录(建立会话)
  25.             login(request, user)
  26.             return redirect('home')
  27.     else:
  28.         form = UserCreationForm()
  29.    
  30.     return render(request, 'register.html', {'form': form})
复制代码
2.2 权限检查

使用 user.has_perm、装饰器或模板变量 perms
2.2.1 视图中的权限检查
  1. from django.contrib.auth.decorators import login_required, permission_required
  2. from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
  3. from django.views.generic import View
  4. from django.contrib.auth.decorators import login_required, permission_required
  5. @login_required  # 要求用户登录
  6. def my_view(request):
  7.     pass
  8. @permission_required('polls.can_vote', raise_exception=True)  # 要求特定权限
  9. def my_vote_view(request):
  10.     pass
  11. # 使用Mixin(类视图)
  12. class PublishView(LoginRequiredMixin, PermissionRequiredMixin, View):
  13.     permission_required = 'myapp.can_publish'
  14.     login_url = '/login/'
  15.    
  16.     def get(self, request):
  17.         return HttpResponse("Book published")
复制代码
2.2.2 模板中的权限检查
  1. {% if perms.myapp.can_publish %}
  2.     <button>Publish Book</button>
  3. {% endif %}
  4. {% if user.has_perm('myapp.can_edit_cover') %}
  5.     <button>Edit Cover</button>
  6. {% endif %}
复制代码
2.3 用户和权限管理

2.3.1 创建用户并分配权限
  1. from django.contrib.auth.models import User, Permission
  2. from django.contrib.contenttypes.models import ContentType
  3. from myapp.models import Book
  4. # 创建用户
  5. user = User.objects.create_user('john', 'john@example.com', 'password')
  6. # 获取权限
  7. content_type = ContentType.objects.get_for_model(Book)
  8. permission = Permission.objects.get(
  9.     codename='can_publish',
  10.     content_type=content_type,
  11. )
  12. # 分配权限给用户
  13. user.user_permissions.add(permission)
  14. # 检查权限
  15. if user.has_perm('myapp.can_publish'):
  16.     print("User can publish books")
复制代码
2.3.2 使用组管理权限
  1. from django.contrib.auth.models import Group, Permission, User
  2. # 1. 创建组并分配权限
  3. editors_group, created = Group.objects.get_or_create(name='Editors')
  4. change_article_perm = Permission.objects.get(codename='change_article')
  5. editors_group.permissions.add(change_article_perm)
  6. # 2. 将用户加入组
  7. user = User.objects.get(username='alice')
  8. user.groups.add(editors_group)
  9. # 现在 user 拥有 editors_group 的所有权限
复制代码
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册