Django-内置用户、权限、分组模块

验证和授权概述:

django有一个内置授权系统,它用来处理用户、分组、权限以及基于cookie的会话系统.

django的授权系统包括验证和授权两个部分。

验证是验证这个用户是否是他声称的人呢(比如用户名和密码验证,角色验证),授权是给与他响应的权限。

Django内置的权限系统包括以下方面:

  1. 用户。
  2. 权限。
  3. 分组。
  4. 一个可以配置的密码哈希系统。
  5. 一个可以插拔的后台管理系统。
    • 比较灵活,想用就用不想用可以不适用。

使用授权系统:

默认中创建完一个django项目后,其实就是已经集成了授权系统。

哪哪部分是跟授权系统相关的配置呢。

下面做一个简单的列表:

INSTALLED_APPS:

  1. django.contrib.auth 包含一个核心授权框架,以及大部分的模型定义。

  2. django.contrib.contenttypes : Content Tpye 系统,可以用来关联模型和权限。

中间件:

  1. SessionMiddleware :用来管理 session.
  2. AuthenticationMiddleware:用来处理当前 session 相关联的用户。
INSTALLED_APPS = [
    'django.contrib.admin',
    #包含一个核心授权框架,以及大部分的模型定义。
    'django.contrib.auth',
    #content type 系统,可以用来关联模型和权限
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',  #用来管理session
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',   #用来处理和当前session相关联的用户。
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

用户对象

User模型

User模型是这个框架的核心部分。他的完整的路径是在django.contrib.auth.models.User。以下对这个User对象做一个简单了解:

字段:

内置的User模型拥有以下的字段:

  • username:用户名。150个字符以内。可以包含数字和英文字符,以及_、@、+、.和-字符。不能为空,且必须唯一!

  • first_name:歪果仁的first_name,在30个字符以内。可以为空。

  • last_name:歪果仁的last_name,在150个字符以内。可以为空。

  • email:邮箱。可以为空。

  • password:密码。经过哈希过后的密码。

  • groups:分组。一个用户可以属于多个分组,一个分组可以拥有多个用户。groups这个字段是跟Group的一个多对多的关系。

  • user_permissions:权限。一个用户可以拥有多个权限,一个权限可以被多个用户所有用。和Permission属于一种多对多的关系。

  • is_staff:是否可以进入到admin的站点。代表是否是员工。

  • is_active:是否是可用的。对于一些想要删除账号的数据,我们设置这个值为False就可以了,而不是真正的从数据库中删除。

  • is_superuser:是否是超级管理员。如果是超级管理员,那么拥有整个网站的所有权限。

  • last_login:上次登录的时间。

  • date_joined:账号创建的时间。

Models类

from django.contrib import auth
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail
from django.db import models
from django.db.models.manager import EmptyManager
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from .validators import UnicodeUsernameValidator


def update_last_login(sender, user, **kwargs):
    """
    A signal receiver which updates the last_login date for
    the user logging in.
    """
    user.last_login = timezone.now()
    user.save(update_fields=['last_login'])


class PermissionManager(models.Manager):
    use_in_migrations = True

    def get_by_natural_key(self, codename, app_label, model):
        return self.get(
            codename=codename,
            content_type=ContentType.objects.db_manager(self.db).get_by_natural_key(app_label, model),
        )


class Permission(models.Model):
    """
    The permissions system provides a way to assign permissions to specific
    users and groups of users.

    The permission system is used by the Django admin site, but may also be
    useful in your own code. The Django admin site uses permissions as follows:

        - The "add" permission limits the user's ability to view the "add" form
          and add an object.
        - The "change" permission limits a user's ability to view the change
          list, view the "change" form and change an object.
        - The "delete" permission limits the ability to delete an object.

    Permissions are set globally per type of object, not per specific object
    instance. It is possible to say "Mary may change news stories," but it's
    not currently possible to say "Mary may change news stories, but only the
    ones she created herself" or "Mary may only change news stories that have a
    certain status or publication date."

    Three basic permissions -- add, change and delete -- are automatically
    created for each Django model.
    """
    name = models.CharField(_('name'), max_length=255)
    content_type = models.ForeignKey(
        ContentType,
        models.CASCADE,
        verbose_name=_('content type'),
    )
    codename = models.CharField(_('codename'), max_length=100)
    objects = PermissionManager()

    class Meta:
        verbose_name = _('permission')
        verbose_name_plural = _('permissions')
        unique_together = (('content_type', 'codename'),)
        ordering = ('content_type__app_label', 'content_type__model',
                    'codename')

    def __str__(self):
        return "%s | %s | %s" % (
            self.content_type.app_label,
            self.content_type,
            self.name,
        )

    def natural_key(self):
        return (self.codename,) + self.content_type.natural_key()
    natural_key.dependencies = ['contenttypes.contenttype']


class GroupManager(models.Manager):
    """
    The manager for the auth's Group model.
    """
    use_in_migrations = True

    def get_by_natural_key(self, name):
        return self.get(name=name)


class Group(models.Model):
    """
    Groups are a generic way of categorizing users to apply permissions, or
    some other label, to those users. A user can belong to any number of
    groups.

    A user in a group automatically has all the permissions granted to that
    group. For example, if the group 'Site editors' has the permission
    can_edit_home_page, any user in that group will have that permission.

    Beyond permissions, groups are a convenient way to categorize users to
    apply some label, or extended functionality, to them. For example, you
    could create a group 'Special users', and you could write code that would
    do special things to those users -- such as giving them access to a
    members-only portion of your site, or sending them members-only email
    messages.
    """
    name = models.CharField(_('name'), max_length=80, 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,)


class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        """
        Create and save a user with the given username, email, and password.
        """
        if not username:
            raise ValueError('The given username must be set')
        email = self.normalize_email(email)
        username = self.model.normalize_username(username)
        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):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(username, email, password, **extra_fields)


# A few helper functions for common logic between User and AnonymousUser.
def _user_get_all_permissions(user, obj):
    permissions = set()
    for backend in auth.get_backends():
        if hasattr(backend, "get_all_permissions"):
            permissions.update(backend.get_all_permissions(user, obj))
    return permissions


def _user_has_perm(user, perm, obj):
    """
    A backend can raise `PermissionDenied` to short-circuit permission checking.
    """
    for backend in auth.get_backends():
        if not hasattr(backend, 'has_perm'):
            continue
        try:
            if backend.has_perm(user, perm, obj):
                return True
        except PermissionDenied:
            return False
    return False


def _user_has_module_perms(user, app_label):
    """
    A backend can raise `PermissionDenied` to short-circuit permission checking.
    """
    for backend in auth.get_backends():
        if not hasattr(backend, 'has_module_perms'):
            continue
        try:
            if backend.has_module_perms(user, app_label):
                return True
        except PermissionDenied:
            return False
    return False


class PermissionsMixin(models.Model):
    """
    Add the fields and methods necessary to support the Group and Permission
    models using the ModelBackend.
    """
    is_superuser = models.BooleanField(
        _('superuser status'),
        default=False,
        help_text=_(
            'Designates that this user has all permissions without '
            'explicitly assigning them.'
        ),
    )
    groups = models.ManyToManyField(
        Group,
        verbose_name=_('groups'),
        blank=True,
        help_text=_(
            'The groups this user belongs to. A user will get all permissions '
            'granted to each of their groups.'
        ),
        related_name="user_set",
        related_query_name="user",
    )
    user_permissions = models.ManyToManyField(
        Permission,
        verbose_name=_('user permissions'),
        blank=True,
        help_text=_('Specific permissions for this user.'),
        related_name="user_set",
        related_query_name="user",
    )

    class Meta:
        abstract = True

    def get_group_permissions(self, obj=None):
        """
        Return a list of permission strings that this user has through their
        groups. Query all available auth backends. If an object is passed in,
        return only permissions matching this object.
        """
        permissions = set()
        for backend in auth.get_backends():
            if hasattr(backend, "get_group_permissions"):
                permissions.update(backend.get_group_permissions(self, obj))
        return permissions

    def get_all_permissions(self, obj=None):
        return _user_get_all_permissions(self, obj)

    def has_perm(self, perm, obj=None):
        """
        Return True if the user has the specified permission. Query all
        available auth backends, but return immediately if any backend returns
        True. Thus, a user who has permission from a single auth backend is
        assumed to have permission in general. If an object is provided, check
        permissions for that object.
        """
        # Active superusers have all permissions.
        if self.is_active and self.is_superuser:
            return True

        # Otherwise we need to check the backends.
        return _user_has_perm(self, perm, obj)

    def has_perms(self, perm_list, obj=None):
        """
        Return True if the user has each of the specified permissions. If
        object is passed, check if the user has all required perms for it.
        """
        return all(self.has_perm(perm, obj) for perm in perm_list)

    def has_module_perms(self, app_label):
        """
        Return True if the user has any permissions in the given app label.
        Use simlar logic as has_perm(), above.
        """
        # Active superusers have all permissions.
        if self.is_active and self.is_superuser:
            return True

        return _user_has_module_perms(self, app_label)


class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """
    
    #用户名
    username_validator = UnicodeUsernameValidator()
        #设置为唯一,并且以后验证用户名和密码是否正确的时候就是用的username
    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    #姓
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    #名
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    #是否是员工
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    #是否是可用
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    #创建时间,什么时候加入的
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)


class User(AbstractUser):
    """
    Users within the Django authentication system are represented by this
    model.

    Username, password and email are required. Other fields are optional.
    """
    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'


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

    def __str__(self):
        return 'AnonymousUser'

    def __eq__(self, other):
        return isinstance(other, self.__class__)

    def __hash__(self):
        return 1  # instances always return the same hash value

    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.")

    @property
    def groups(self):
        return self._groups

    @property
    def user_permissions(self):
        return self._user_permissions

    def get_group_permissions(self, obj=None):
        return set()

    def get_all_permissions(self, obj=None):
        return _user_get_all_permissions(self, obj=obj)

    def has_perm(self, perm, obj=None):
        return _user_has_perm(self, perm, obj=obj)

    def has_perms(self, perm_list, obj=None):
        for perm in perm_list:
            if not self.has_perm(perm, obj):
                return False
        return True

    def has_module_perms(self, module):
        return _user_has_module_perms(self, module)

    @property
    def is_anonymous(self):
        return True

    @property
    def is_authenticated(self):
        return False

    def get_username(self):
        return self.username

User模型的基本用法

创建用户

通过create_user方法可以快速的创建用户。这个方法必须要传递username、email、password。

示例代码如下:

from django.contrib.auth.models import User
user = User.objects.create_user('zhiliao','[email protected]','111111')
# 此时user对象已经存储到数据库中了。当然你还可以继续使用user对象进行一些修改
user.last_name = 'abc'
user.save()

创建超级用户

创建超级用户有两种方式。第一种是使用代码的方式。用代码创建超级用户跟创建普通用户非常的类似,只不过是使用create_superuser。示例代码如下:

from django.contrib.auth.models import User
User.objects.create_superuser('admin','[email protected]','111111')

也可以通过命令行的方式。命令如下:

python manage.py createsuperuser

后面就会提示你输入用户名、邮箱以及密码。

#需要输入的命令
C:\Users\Administrator\Desktop\Django项目\Students_comm>python manage.py createsuperuser
Username (leave blank to use 'administrator'):      #用户名
Email address:                 #emali 可以为空
Password:                 #密码:不可以纯数字,低于6位数
Password (again):          #确认密码
Superuser created successfully.          #提示超级用户创建成功

修改密码

因为密码是需要经过加密后才能存储进去的。所以如果想要修改密码,不能直接修改password字段,而需要通过调用set_password来达到修改密码的目的。示例代码如下:

from django.contrib.auth.models import User
user = User.objects.get(pk=1)
user.set_password('新的密码')
user.save()

登录验证

Django的验证系统已经帮我们实现了登录验证的功能。通过django.contrib.auth.authenticate即可实现。这个方法只能通过username和password来进行验证。示例代码如下:

from django.contrib.auth import authenticate
user = authenticate(username='zhiliao', password='111111')
# 如果验证通过了,那么就会返回一个user对象。
if user is not None:
    # 执行验证通过后的代码
else:
    # 执行验证没有通过的代码。

扩展用户模型

在中国一般都是使用手机号邮箱等其他方式来登录,一般不适用用户名,所以需要拓展模型。

Django内置的User模型虽然已经足够强大了。但是有时候还是不能满足我们的需求。比如在验证用户登录的时候,他用的是用户名作为验证,而我们有时候需要通过手机号码或者邮箱来进行验证。还有比如我们想要增加一些新的字段。那么这时候我们就需要扩展用户模型了。扩展用户模型有多种方式。这里我们来一一讨论下。

1.设置Proxy模型:

第一种方式使用代理,可插拔。

如果你对Django提供的字段,以及验证的方法都比较满意,没有什么需要改的。但是只是需要在他原有的基础之上增加一些操作的方法。那么建议使用这种方式。示例代码如下:

class Person(User):
    class Meta:
        proxy = True
 
    def get_blacklist(self):
        return self.objects.filter(is_active=False)

在以上,我们定义了一个Person类,让他继承自User,并且在Meta中设置proxy=True,说明这个只是User的一个代理模型。他并不会影响原来User模型在数据库中表的结构。以后如果你想方便的获取所有黑名单的人,那么你就可以通过Person.get_blacklist()就可以获取到。并且User.objects.all()和Person.objects.all()其实是等价的。因为他们都是从User这个模型中获取所有的数据。

2.一对一外键:

如果你对用户验证方法authenticate没有其他要求,就是使用username和password即可完成。但是想要在原来模型的基础之上添加新的字段,那么可以使用一对一外键的方式。示例代码如下:

from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save

class UserExtension(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='extension')
    birthday = models.DateField(null=True, blank=True)
    school = models.CharField(max_length=100)

#监听user的变化,括号内第一个值为接收什么新号 第二个值为接收谁发出的新号
@receiver(post_save, sender=User)
#第三个值为是否是新创建的。
def create_user_extension(sender, instance, created, **kwargs):
    #如果是第一次创建,那就创建一个userex进行绑定。
    if created:
        UserExtension.objects.create(user=instance)
    #如果不是第一次创建,将进行保存
    else:
        instance.extension.save()

以上定义一个UserExtension的模型,并且让她和User模型进行一对一的绑定,以后我们新增的字段,就添加到UserExtension上。并且还写了一个接受保存模型的信号处理方法,只要是User调用了save方法,那么就会创建一个UserExtension和User进行绑定。

一对一的关系会更加的安全。

这种方法不会破坏原有的user,进行实际开发的时候建议使用这种方法。

就是比较麻烦一点。

3.继承自AbstractUser:

对于authenticate不满意,并且不想要修改原来User对象上的一些字段,但是想要增加一些字段,那么这时候可以直接继承自django.contrib.auth.models.AbstractUser,其实这个类也是django.contrib.auth.models.User的父类。比如我们想要在原来User模型的基础之上添加一个telephone和school字段。示例代码如下:

from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    telephone = models.CharField(max_length=11,unique=True)
    school = models.CharField(max_length=100)

    # 指定telephone作为USERNAME_FIELD,以后使用authenticate
    # 函数验证的时候,就可以根据telephone来验证
    # 而不是原来的username
    USERNAME_FIELD = 'telephone'
    REQUIRED_FIELDS = []

    # 重新定义Manager对象,在创建user的时候使用telephone和
    # password,而不是使用username和password
    objects = UserManager()


class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self,telephone,password,**extra_fields):
        if not telephone:
            raise ValueError("请填入手机号码!")
        user = self.model(telephone=telephone,*extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_user(self,telephone,password,**extra_fields):
        extra_fields.setdefault('is_superuser',False)
        return self._create_user(telephone,password)

    def create_superuser(self,telephone,password,**extra_fields):
        extra_fields['is_superuser'] = True
        return self._create_user(telephone,password)

然后再在settings中配置好AUTH_USER_MODEL=youapp.User

这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate前就先定义好。

4.继承自AbstractBaseUser模型:

如果你想修改默认的验证方式,并且对于原来User模型上的一些字段不想要,那么可以自定义一个模型,然后继承自AbstractBaseUser,再添加你想要的字段。这种方式会比较麻烦,最好是确定自己对Django比较了解才推荐使用。步骤如下:

1,创建模型。示例代码如下:
class User(AbstractBaseUser,PermissionsMixin):
     email = models.EmailField(unique=False)
     username = models.CharField(max_length=150)
     telephone = models.CharField(max_length=11,unique=True)
     is_active = models.BooleanField(default=True)
 
     USERNAME_FIELD = 'telephone'
     REQUIRED_FIELDS = []
 
     objects = UserManager()
 
     def get_full_name(self):
         return self.username
 
     def get_short_name(self):
         return self.username

其中passwordlast_login是在AbstractBaseUser中已经添加好了的,我们直接继承就可以了。然后我们再添加我们想要的字段。比如emailusernametelephone等。这样就可以实现自己想要的字段了。但是因为我们重写了User,所以应该尽可能的模拟User模型:

  • USERNAME_FIELD:用来描述User模型名字字段的字符串,作为唯一的标识。如果没有修改,那么会使用USERNAME来作为唯一字段。
  • REQUIRED_FIELDS:一个字段名列表,用于当通过createsuperuser管理命令创建一个用户时的提示。
  • is_active:一个布尔值,用于标识用户当前是否可用。
  • get_full_name():获取完整的名字。
  • get_short_name():一个比较简短的用户名。
2,重新定义UserManager:

我们还需要定义自己的UserManager,因为默认的UserManager在创建用户的时候使用的是username和password,那么我们要替换成telephone。示例代码如下:

 class UserManager(BaseUserManager):
     use_in_migrations = True

     def _create_user(self,telephone,password,**extra_fields):
         if not telephone:
             raise ValueError("请填入手机号码!")
         user = self.model(telephone=telephone,*extra_fields)
         user.set_password(password)
         user.save()
         return user

     def create_user(self,telephone,password,**extra_fields):
         extra_fields.setdefault('is_superuser',False)
         return self._create_user(telephone,password)

     def create_superuser(self,telephone,password,**extra_fields):
         extra_fields['is_superuser'] = True
         return self._create_user(telephone,password)
3.设置Setting

在创建了新的User模型后,还需要在settings中配置好。配置AUTH_USER_MODEL='appname.User'

4.使用自定义模型

如何使用这个自定义的模型:比如以后我们有一个Article模型,需要通过外键引用这个User模型,那么可以通过以下两种方式引用。
第一种就是直接将User导入到当前文件中。示例代码如下:

 from django.db import models
 from myauth.models import User
 class Article(models.Model):
     title = models.CharField(max_length=100)
     content = models.TextField()
     author = models.ForeignKey(User, on_delete=models.CASCADE)

这种方式是可以行得通的。但是为了更好的使用性,建议还是将User抽象出来,使用settings.AUTH_USER_MODEL来表示。示例代码如下:

 from django.db import models
 from django.conf import settings
 class Article(models.Model):
     title = models.CharField(max_length=100)
     content = models.TextField()
     author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate前就先定义好。

权限和分组

登录、注销和登录限制:

登录

在使用authenticate进行验证后,如果验证通过了。那么会返回一个user对象,拿到user对象后,可以使用django.contrib.auth.login进行登录。示例代码如下:

  • 切记:这里不可以定义视图名字为login。
  • 可以使用其他的名字。
user = authenticate(username=username, password=password)
if user is not None:
    if user.is_active:
      #login登录之后自动把用户信息存入session
        login(request, user)

实例:

from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponse
from django.shortcuts import render,redirect
from url_app.forms import LoginForm
from django.contrib.auth.decorators import login_required

# 登录页面
def my_login(request):
    if request.method  == 'GET':
        return render(request,'login.html',locals())
    else:
        form = LoginForm(request.POST)
        if form.is_valid():
            telephone = form.cleaned_data.get('telephone')
            password = form.cleaned_data.get('password')
            remember = form.cleaned_data.get('remember')
            user = authenticate(request,username=telephone,password=password)
            #判断user是否存在和是否是活跃状态。
            if user and user.is_active:
                #如果是将 保存session
                login(request,user)
                if remember:
                    #如果如果过用户选择记住密码则将session状态为永久,最高存储15天
                    request.session.set_expiry(None)
                else:
                    #如果没有选记住密码在用户关闭之后清空session
                    request.session.set_expiry(0)
                                        
                return HttpResponse('登录成功!')
            else:
                return HttpResponse('手机号或者密码错误')
        else:
            return redirect(my_login)

注销:

注销,或者说退出登录。我们可以通过django.contrib.auth.logout来实现。他会清理掉这个用户的session数据。

实例:

def my_logout(request):
    #在用户登录状态具有清空当前session的作用。
    logout(request)
    return HttpResponse('')

基于角色的权限系统

​ 现在各大系统都采用的是基于角色的权限控制,这里就涉及到三个东西:用户、角色、资源(权限),在Django中就是:用户、用户组、权限。用户和角色的关系一般为多对多,角色和资源的关系也为多对多,如下图(此图来源于互联网)

​ 这样设计有一个好处,就是在系统越来越大的时候如果给每个用户逐一赋予权限非常麻烦和繁琐,只需要给角色赋予相应的权限用户赋予他对应的角色即可,如果有新的需求只需要添加有相应权限的角色即可。

Django权限机制的实现

1、不依赖于Django中的权限模型

​ 设计三个实体类User、Role、Resource分别对应上面提出的用户、角色、资源,User和Resource之间为多对多的关系,Role和Resource之间为多对多的关系。User中封装的是用户的信息如用户名密码等,Resource可以封装权限标识(后面再进行分析)也可以封装允许访问的URL地址。

​ 编写装饰器对视图处理方法进行拦截

  • 资源封装URL

​ 在装饰器中获取当前访问的URL,取出当前用户(从Session中取,前题是在登录的时候需要把用户信息放去Session中去),迭代判断用户的所有角色绑定的资源中的URL,如果存在与当前访问URL相同的地址则放行,否则则跳转到无权限的页面。

弊端:如果URL发生了变动需要修改资源(权限)

  • 资源封装权限标识

​ 在装饰器标示在视图处理方法上时传入权限标识参数(如:@auth("user:add")),在装饰器中也是从Session中获取用户,迭代用户的所有角色绑定的资源中的权限标识,如果与传入装饰器中的权限标识相同则放行,否则跳转到无权限的页面。

好处:如果URL发生了变动无需修改资源(权限),Django内部的权限系统就是采用的这种方式,Java目前越来越流行的权限控制框架Shiro也是采用的这种方式

2、依赖于Django中的权限模型

​ Django用User、Group、Permission来表示上面的用户、角色、资源(权限),在Django中不管你是否使用其自带的权限控制只要你继承了他的模型类(models.Model)会默认在auth_permission表中插入三个权限信息(以Book为例,会插入如下三个权限信息:add_book、change_book、delete_book,分别代表了添加、修改、删除三个权限),如果需要更多的权限信息可以在定义实体的时候如下定义:

class Book(models.Model):
    name = models.CharField()

    class Meta:
        permissions = (
            ('自定义的权限标识', '权限说明信息'),
        )

​ 每个permission都是django.contrib.auth.Permission类型的实例,该类型包含三个字段name, codename 和 content_type,其中 content_type反应了permission属于哪个model(如上就是Book),codename就是权限标识,代码逻辑中检查权限时要用, name是permission的描述显示的时候会用到。

​ 权限检测装饰器:request.user封装了当前登录系统的用户

from django.contrib.auth.decorators import permission_required

@permission_required('应用名.权限标识')
def view(request):
    ....

​ 在模版中使用:模版中使用全局变量perms存储当前用户的所有权限

{% if perms.应用名.权限标识 %}
    <!-- 这里是有权限才显示的内容 -->
{% endif %}

权限:

Django中内置了权限的功能。他的权限都是针对表或者说是模型级别的。比如对某个模型上的数据是否可以进行增删改查操作。他不能针对数据级别的,比如对某个表中的某条数据能否进行增删改查操作(如果要实现数据级别的,考虑使用django-guardian)。创建完一个模型后,针对这个模型默认就有三种权限,分别是增/删/改/。可以在执行完migrate命令后,查看数据库中的auth_permission表中的所有权限。

其中的codename表示的是权限的名字。name表示的是这个权限的作用。

通过定义模型添加权限:

如果我们想要增加新的权限,比如查看某个模型的权限,那么我们可以在定义模型的时候在Meta中定义好。示例代码如下:

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    author = models.ForeignKey(get_user_model(),on_delete=models.CASCADE)

    class Meta:
        permissions = (
            ('view_article','can view article'),
        )

通过代码添加权限:

权限都是django.contrib.auth.Permission的实例。这个模型包含三个字段,namecodename以及content_type,其中的content_type表示这个permission是属于哪个app下的哪个models。用Permission模型创建权限的代码如下:

from django.contrib.auth.models import Permission,ContentType
from .models import Article
content_type = ContentType.objects.get_for_model(Article)
permission = Permission.objects.create(name='可以编辑的权限',codename='edit_article',content_type=content_type)

实例:

#创建权限的第二种方法通过实例模型创建权限
#引入权限表
from django.contrib.contenttypes.models import ContentType
#引入models类
from url_app.models import Article
from django.contrib.auth.models import Permission
def add_permission(request):
    content_type = ContentType.objects.get_for_model(Article)
    '''
    codename  权限的名称
    name  权限的描述
    content_tpye 指定那个表的权限
    '''
    permission = Permission.objects.create(codename='black_article',name='拉黑文章',content_type=content_type)
    return HttpResponse('权限创建成功!')

用户与权限管理:

权限本身只是一个数据,必须和用户进行绑定,才能起到作用。User模型和权限之间的管理,可以通过以下几种方式来管理:

  1. myuser.user_permissions.set(permission_list):直接给定一个权限的列表。
  2. myuser.user_permissions.add(permission,permission,...):一个个添加权限。
  3. myuser.user_permissions.remove(permission,permission,...):一个个删除权限。
  4. myuser.user_permissions.clear():清除权限。
  5. myuser.has_perm('<app_name>.<codename>'):判断是否拥有某个权限。权限参数是一个字符串,格式是app_name.codename
  6. myuser.get_all_permissons():获取所有的权限。

实例:

def operate_permission(request):
    user = User.objects.get(telephone='1111')
    #可以根据content_tpye 的外键关联进行添加
    content_type = ContentType.objects.get_for_model(Article)
    permissions = Permission.objects.filter(content_type=content_type)

    #里面传递的是一个可迭代对象,上面的permission是一个列表,所以一次性可以将有关这个表的权限全部传给他
    user.user_permissions.set(permissions)
    user.save()
    #删除用户所有权限
    # user.user_permissions.clear()

    #一次性添加一个权限
    # user.user_permissions.add(permissions[0])
    # 通过给加星号的方法可以把permissions打散开来,在添加进去
    # *[1,2,3]  = 1,2,3
    # user.user_permissions.add(*permissions)

    #删除一个权限
    # user.user_permissions.remove(permissions[0])


    # 获取该用户的所有权限
    # print(user.get_all_permissions())


    return HttpResponse('操作权限完成!!!')

权限限定装饰器:

使用django.contrib.auth.decorators.permission_required可以非常方便的检查用户是否拥有这个权限,如果拥有,那么就可以进入到指定的视图函数中,如果不拥有,那么就会报一个400错误。示例代码如下:

from django.contrib.auth.decorators import permission_required

@permission_required('front.view_article')
def my_view(request):
    ...

实例:

from django.contrib.auth.decorators import permission_required
#这个装饰器做两件事情
#首先是判断你有没有登录,如果没有登录他就给你跳转到登录页面
#如果登录了再次判断你有没有这个权限,如果没有这个权限将给你跳转到登录页面
#如果 raice_exception = True 就会给你跳转到403的页面,可对这个页面进行编辑
@permission_required('url_app.add_article',login_url='/login/',raise_exception=True)
def add_article(request):
    # if request.user.is_authenticated:
    #     print('已经登录了!')
    #     if request.user.has_perm('url_app.add_article'):
    #         return HttpResponse('这是添加文章的页面!')
    #     else:
    #         return HttpResponse('你没有访问这个文章的权限!!')
    # else:
    #     return HttpResponse('很抱歉你没有登录!!')

    return HttpResponse('这是添加文章的界面!!')

分组:

权限有很多,一个模型就有最少三个权限,如果一些用户拥有相同的权限,那么每次都要重复添加。这时候分组就可以帮我们解决这种问题了,我们可以把一些权限归类,然后添加到某个分组中,之后再把和把需要赋予这些权限的用户添加到这个分组中,就比较好管理了。分组我们使用的是django.contrib.auth.models.Group模型, 每个用户组拥有idname两个字段,该模型在数据库被映射为auth_group数据表。

分组操作:

  1. Group.object.create(group_name):创建分组。
  2. group.permissions:某个分组上的权限。多对多的关系。
    • group.permissions.add:添加权限。
    • group.permissions.remove:移除权限。
    • group.permissions.clear:清除所有权限。
    • user.get_group_permissions():获取用户所属组的权限。
  3. user.groups:某个用户上的所有分组。多对多的关系。
from django.contrib.auth.models import Group
def operate_group(request):
    #创建一个分组
    #组和权限是多对多关系 多个组可以拥有多个权限
    # group = Group.objects.create(name='运营')
    # content_type = ContentType.objects.get_for_model(Article)
    # permissions = Permission.objects.filter(content_type=content_type)
    #
    # group.permissions.set(permissions)
    # group.save()

    #一个组拥有了权限 一个 用户拥有了组 不就等同于一个用户拥有了权限吗??
    #给指定用户添加分组,用户和分组是多对多关系一个用户可以拥有多个组
    # 定义user的时候并没有定义group那么 group是哪里来的??
    # group = Group.objects.get(name='运营')
    user = User.objects.get(telephone='123123')
    # user.groups.add(group)
    # user.save()

    # 获取用户所属组的权限。
    print(user.get_group_permissions())

    #user.has_perm:
    # user.has_perms  可以判断多个权限

    # def has_perms(self, perm_list, obj=None):
    #     """
    #     Return True if the user has each of the specified permissions. If
    #     object is passed, check if the user has all required perms for it.
    #     """
    #     return all(self.has_perm(perm, obj) for perm in perm_list)

    #1.首先判断user.permissions 下有没有这个权限,如果有:就True
    #2.如果过user.permission下没有这个权限,就会判断所属分组下有没有这个权限如果有则还是True
    return HttpResponse('操作分组!')
'''
判断权限的装饰器也可以判断是否拥有多个权限那就是把权限放在一个列表中
'''

在模板中使用权限:

settings.TEMPLATES.OPTIONS.context_processors下,因为添加了django.contrib.auth.context_processors.auth上下文处理器,因此在模板中可以直接通过perms来获取用户的所有权限。

示例代码如下:

{% if perms.应用名.权限标识 %}
    <!-- 这里是有权限才显示的内容 -->
{% endif %}

猜你喜欢

转载自www.cnblogs.com/i969639/p/11201257.html