Django Auth用户与用户组详述

在第一章的《Django auth应用模块》我们简单的介绍了 auth 应用,它能够帮助开发者快速构建用户模块的基本功能,包括用户与用户组的实现以及定义用户与用户组权限等,例如,社交平台需要现有用户才可以发布动态话题;管理后台 admin 需要有用户才能登陆等。而且对于不同的用户,Web 站点还可以提供不同的服务,这就是权限的概念。

Django 框架内置的
用户认证系统实现了上述功能,即身份的验证和权限的管理,与其他的内置的模块类似,这套系统能够很好的支持扩展和自定义功能,本章我们将一起认识 Django 的用户认证系统。
用户认证系统中定义了三个 Model 用来标识用户与用户关系,分别是 User(用户)、AnonymousUser(匿名用户)和 Group(用户组),它们都定义在下面的路径文件中

django/contrib/auth/models.py

1. User用户模型

在《Django Admin数据表可视化》一节,我们使用 createsuperuser 命令创建了超级用户,在视图函数中我们可以通过
HttpRequest 的 user 属性获取当前的登录用户。这里的用户其实就是 Django 框架中内置的 User Model(即auth_user表)因为它被定义在 auth 应用下所以表名是auth_user,可用如下方式引入 User 模型:

from django.contrib.auth.models import User

在《Django auth应用模块》中我们提到过 auth_user 表,在执行完毕 migrate 后,它的表结构如下所示:

+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| password     | varchar(128) | NO   |     | NULL    |                |
| last_login   | datetime(6)  | YES  |     | NULL    |                |
| is_superuser | tinyint(1)   | NO   |     | NULL    |                |
| username     | varchar(150) | NO   | UNI | NULL    |                |
| first_name   | varchar(30)  | NO   |     | NULL    |                |
| last_name    | varchar(150) | NO   |     | NULL    |                |
| email        | varchar(254) | NO   |     | NULL    |                |
| is_staff     | tinyint(1)   | NO   |     | NULL    |                |
| is_active    | tinyint(1)   | NO   |     | NULL    |                |
| date_joined  | datetime(6)  | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.02 sec)

对于上表前面介绍是我们只是一笔带过,在这里有必讲解一下需要重点理解的属性。如下所示:

  • is_superuser:布尔值,默认值是 False。标识是否是超级用户,代表用户拥有所有权限。
  • username:用户名,具有唯一性限制,最大长度为150个字符,只可以包含字母、数字、@、.、+、-、_这些字符。
  • password:密码,Django 并不会存储原始密码,其存储的实际是原始密码经过 Hash 散列处理之后的值。
  • is_staff:布尔值,默认为 False。标识用户是否可以访问管理后台。
  • is_active:布尔值,默认值是True。标识当前用户是否处于激活状态。

除了基础属性之外,User 中还定义了与 Group 和 Permission(权限)之间的关联关系:

class PermissionsMixin(models.Model):
group=models.ManyToManyField(Group,...)
user_permission=models.ManyToManyField(Permission,...)

User 关联表,即 auth_user_groups 和 auth_user_user_permissions,其表分别结构如下图所示:

mysql> desc auth_user_groups;
+----------+---------+------+-----+---------+----------------+
| Field    | Type    | Null | Key | Default | Extra          |
+----------+---------+------+-----+---------+----------------+
| id       | int(11) | NO   | PRI | NULL    | auto_increment |
| user_id  | int(11) | NO   | MUL | NULL    |                |
| group_id | int(11) | NO   | MUL | NULL    |                |
+----------+---------+------+-----+---------+----------------+
3 rows in set (0.02 sec)

mysql> desc auth_user_user_permissions;
+---------------+---------+------+-----+---------+----------------+
| Field         | Type    | Null | Key | Default | Extra          |
+---------------+---------+------+-----+---------+----------------+
| id            | int(11) | NO   | PRI | NULL    | auto_increment |
| user_id       | int(11) | NO   | MUL | NULL    |                |
| permission_id | int(11) | NO   | MUL | NULL    |                |
+---------------+---------+------+-----+---------+----------------+
3 rows in set (0.02 sec)

1) User模型创建用户与超级用户

我们可以使用 User 模型的 create_user 和 create_superuser 分别创建用户或者是超级用户,在创建超级用户的时候需要注意 is_staff 与 is_superuserd 的 bool 值,需要都设置为 Ture 才可以。下面看一下查看其中一个方法即 create_user 的源代码,通过查看,加深对 Django 的学习,如下所示:

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        """
        创建并保存具有给定用户名、电子邮件和密码的用户。
        """
        if not username:
            raise ValueError('The given username must be set')
        email = self.normalize_email(email)
        #使用户名规范化调用normalize_username
        username = self.model.normalize_username(username)
        #新建user实例
        user = self.model(username=username, email=email, **extra_fields)
        #设置密码的方法
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, username, email=None, password=None, **extra_fields):
        #普通用户的is_staff和is_superuser都为False
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(username, email, password, **extra_fields)

使用该方法的实例如下所示:

from django.contrib.auth.models import User
user=User.objects.create_user('bookstore','123@163.com','python_django')

同样我们可以 set_password() 方法修改密码,最后记得调用 save() 方法保存即可。

2. AnonymousUser匿名用户模型

对于 AnonymousUser ,它的常见用法是对视图的请求,对于未登陆的用户,request 的 user 属性即指向了 AnonymousUser 表示匿名用户,我们看一下这个类是如何实现的:

class AnonymousUser:
    id = None
    pk = None
    username = ''
    is_staff = False
    is_active = False
    is_superuser = False
    _groups = EmptyManager(Group)
    _user_permissions = EmptyManager(Permission)

从源码分析可以看出 AnonymousUser 定义匿名用户的主要属性, 可以看到它的 is_staff 和 is_active 以及 is_superuser 都设置成为了 False,它还定义了一些方法如下所示:

def save(self):
    raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")

def delete(self):
    raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")

def set_password(self, raw_password):
    raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")

def check_password(self, raw_password):
    raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")

从上述代码可以看出 AnonymousUser 匿名用户定义的方法都抛出了 NotImplementedError 异常,所以它并没实现任何方法。

3. Group用户组模型

上面讲解了用户模型与匿名用户模型,最后一个模型就是用户组 Group,首先我们来理解一下用户组的概念。

1) Group用户组概念

组是对用户进行分类的通用方法,以便将权限或其他标签应用到这些用户。用户可以属于任意数量的组。组中的用户自动拥有授予该组的所有权限。例如,如果“网站编辑”组有权限 can_edit_home_page,该组中的任何用户都将拥有该权限。除了权限之外,组还可以方便地对用户进行分类,以便对他们应用一些标签或扩展功能。例如,您可以创建一个“特殊用户”组,并且您可以编写相应的代码对这个特殊用户组,让组内用户做一些特殊的事情——比如让他们访问您站点的成员权限部分,或者给他们发送成员权限的电子邮件消息。

然后我们可以查看一下 Django 实现 Group 编写的源码部分。如下所示:

class Group(models.Model):
    name = models.CharField(_('name'), max_length=150, unique=True)
    permissions = models.ManyToManyField(
        Permission,
        verbose_name=_('permissions'),
        blank=True,
    )

    objects = GroupManager()

    class Meta:
        verbose_name = _('group')
        verbose_name_plural = _('groups')

    def __str__(self):
        return self.name

    def natural_key(self):
        return (self.name,)

从源码解析来看,Group 用户组之定义了一个字段 name,代表用户组的名称而且必须具有唯一性,其最大字符长度为 150,它还定义与 Permission 模型之间多对多关联关系,那么它们之间就有有一张中间表即 auth_group_permissions,通过数据库查看一下它的表结构,如下所示:

mysql> desc auth_group_permissions;
+---------------+---------+------+-----+---------+----------------+
| Field         | Type    | Null | Key | Default | Extra          |
+---------------+---------+------+-----+---------+----------------+
| id            | int(11) | NO   | PRI | NULL    | auto_increment |
| group_id      | int(11) | NO   | MUL | NULL    |                |
| permission_id | int(11) | NO   | MUL | NULL    |                |
+---------------+---------+------+-----+---------+----------------+

2) Group用户组实例应用

下面我们创建一个用户组名称为 reader,然后将 user 加入到该组当中:

In [1]: from django.contrib.auth.models import User,Group
In [2]: group=Group.objects.create(name="reader")
In [3]: user=User.objects.get(username="bookstore")
In [4]: user.groups.add(group)
In [5]: user.groups.all()
Out[5]: <QuerySet [<Group: reader>]>

通过上述的代码就将用户 user 加入到了组 reader 中,我们可以通过用户组权限再给这个组设置相应的权限,查看 auth_user_groups 表可得如下结果:

mysql> select * from auth_user_groups;
+----+---------+----------+
| id | user_id | group_id |
+----+---------+----------+
|  1 |       2 |        1 |
+----+---------+----------+

本节我们详细介绍了 Django 用户认证系统中的用户与用户组,从源码的角度出发对它们之间的关联关系进行了深度的剖析,通过本节的讲解大家对用户与用户组的概念不在感到陌生,在下一节我们将讲解如何进行用户的身份认证。

匿名

发表评论

匿名网友