django的认证与授权系统

0.概述

Django 有一个内置的授权系统。他用来处理用户、分组、权限以及基于 cookie 的会话系统。 Django 的授权系统包括验证和授权两个部分。验证是验证这个用户是否是他声称的人(比如用户名和密码验证,角色验证),授权是给与他相应的权限。 Django 内置的权限系统包括以下方面:

1. 用户。
2. 权限。
3. 分组。
4. 一个可以配置的密码哈希系统。
5. 一个可插拔的后台管理系统。

内置系统的使用:默认中创建完一个 django 项目后,其实就已经集成了授权系统。那哪些部分是跟授权系统相关的配置呢。以下做一个简单列表

INSTALLED_APPS  :
    1.  django.contrib.auth  :包含了一个核心授权框架,以及大部分的模型定义。
    2.  django.contrib.contenttypes  : Content Type  系统,可以用来关联模型和权限。
中间件:
    1.  SessionMiddleware  :用来管理 session  。
    2.  AuthenticationMiddleware  :用来处理和当前 session  相关联的用户

1.关于认证与授权的本质

  实质上是把用户存在数据库中的数据和通过表单提交上来的数据做对比,看是否相等。认证的作用是确定用户的身份,比如是否是公司的员工,没是否是注册用户。而权限作用确定用户能够做什么,比如用户是否可以浏览付费咨询,如果后台系统的话对于员工的话,是否可以修改编辑某些文件等。

2.django内置了验证系统-----auth

(1)authenticate(传入用户名和密码实现用户的登录,验证成功后返回一个user对象)

(2)login(完成具体的登录过程)

   该函数接受一个HttpRequest对象 ,以及一个认证了的User对象(通过authenticate验证的独享),此函数使用django的session框架给某个已认证的用户附加上session id等信息。(在响应头里面对数据作出了修改)

(3)logout(退出登录,本质上是封装了函数,调用session的flush()方法,清楚了内容)

3.User对象(user模型是整个框架的核心):

(1)is_authenticated(),判断用户是否登录(利用django的login_required装饰器来操作,登陆成功后就返回到原来的访问页面(装饰器实现的核心代码)):

def  my_view(request):

    if  not  request.user.is_authenticated():
        return  redirect("%s?next=%s"%(settings.LOGIN_URL, request.path))

(2)create_user与create_superuser,需要传入哟邮箱,密码和用户名称参数。

(3)checkpassword,setpassword(密码通过hash算法加密后存储在数据库中)

4.django自定义User:

(1)利用proxy代理模式;------关于ORM模式的代理模式的使用。

  适用条件:如果你对 Django  提供的字段,以及验证的方法都比较满意,没有什么需要改的

  应用:通过设置proxy=true来设置代理模式,应用场景(设置黑名单列表),优点是不影响原理的user表,可以扩展功能:

class Person(User):

    class Meta:
        proxy = True

    def get_blacklist(self):
        return self.objects.filter(is_active=False)       

  后续可以直接调用get_blacklist()获取黑名单。

(2)利用一对一外键;

  适用条件:对于用户验证方法authentica比较满意,利用authenticate来做验证,只是想要拓展字段。

  应用:通过设置一对一外键,可以存在用户表中的不常用的字段,放在扩展字段。想实现利用手机号或者用邮箱来熟悉爱你用户认证登录。

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)

    @receiver(post_save,sender=User)
    def create_user_extension(sender,instance,created,**kwargs):
        if created:
            UserExtension.objects.create(user=instance)
        else:
            instance.extension.save                        

  上面利用信号量机制,当user模型执行保存的时候,就会创建一个UserExtension对象与之绑定。问题:保存数据需要执行两次的操作。

(3)继承自AbstracUser

  适用条件:想要修改authenticate验证方式,在不改变原来字段的基础上增加字段到模型中,通过继承User的父类来生成新的字段。

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)

因为改变了原来的User需要在settings文件中重新做配置:AUTH_USER_MODEL=youapp.User。此外,因为这种方式,破坏了表结构,需要在第一次migrate之前先定义好。

否则会与系统内置的User发生冲突。

(4)继承自Abstractbaseuser

适用条件:修改验证方式,并且决定对原有的字段做修改,删除一些字段。

应用:继承自AbstractBaseUser,可以按照自己的方式来定义和选择字段。

class User(AbstractBaseUser,PermissionsMixin):

    email = models.EmailField(unique=True)
    username = models.CharField(max_length=150)
    telephone = models.CharField(max_length=11,unique=True)
    is_active = models.BooleanField(default=True)
    
    # 用来描述 User  模型名字字段的字符串,作为唯一的标识,默认是 
    # username
    USERNAME_FIELD = 'telephone'
    通过 createsuperuser  管理命令创建一个用户时的提示。
    REQUIRED_FIELDS = []
    # 将修改后的模型管理类赋值给原理的类,后续调用(具体了解django的Manager类的使用)
    objects = UserManager()

    def get_full_name(self):
        return self.username

    def get_short_name(self):
        return self.username     

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)                    

 在创建了新的模型类后,需要seettins中配置好AUTH_USER_MODEL='appname.User'。后续如果想调用User,可以通过“settings.AUTH_USER_MODEL”.

5.权限

权限本质上是存在content_type表中的字段,在表中记录了每个模型所具有的字段(permission)是模型级别的权限,针对表的操作作出限制。通过将用户与权限关联起来,权限判断的本质就是查询数据库,看用户是否具有某项权限。如果有权限就可以执行某个视图,没有,就无法执行该视图,给用户返回提示信息,无法执行该视图。

(1)给用户添加权限:

   通过模型对象的方式来添加:

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 的实例。这个模型包含三个字段, name 、 codename 以及 content_type ,其中的 content_type 表示这个 permission 是属于

哪个 app 下的哪个 models 。

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',con
tent_type=content_type)

(2)权限本身只是一个数据,可以通过下面方法来实现对权限的绑定:

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()  :获取所有的权限

(3)权限限定装饰器(使用 django.contrib.auth.decorators.permission_required 可以非常方便的检查用户是否拥有这个权限,如果拥有,那么就可以进入到指定的视图函数中)

from django.contrib.auth.decorators import permission_required

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

6.分组

为了方便对用户进行批量管理,使用django.contrib.auth.models.Group  模型,每个用户组拥有 id 和 name 两个字段,该模型在数据库被映射为 auth_group 数据表。

把一些权限归类,然后添加到某个分组中,之后再把和把需要赋予这些权限的用户添加到这个分组中。

(1)用户分组操作:

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  :某个用户上的所有分组。多对多的关系。               

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

{% if perms.front.add_article %}
    <a href='/article/add/'>添加文章</a>
{% endif %}

7.总结:

  一个好的程序员不应该只是知道不要重复造轮子,但是应该知道轮子是如何造的,了解一个功能背后的原理,能够设计出适合自己的轮子,这才是优秀的程序员。所谓举一反三,从设计者的角度来思考。要知道代码只是结局问题的一种表达方式,你可以有自己的表达方式。

  在django框架中,ORM模型是核心,授权与验证系统实质上就基于ORM模型来操作。整个系统本质上是对数据库中表的操作,包括以下几个模型(红色框内的):

  

  上述模型,分别存储了用户,权限和组以及组权限的信息。而对于模型的操作,则通过对模型类定义相应的操作方法(本质上是发呢告状了ORM对数据库的操作语句),使用的时候直接调用封装后的函数,制定对模型的操作。

  通过上述分析,了解了auth认证系统的作用,方便直接使用,同时对于其内部原理有了一个简单的了解,可以利用面向对象的特性,如同对于User模型进行扩展,可以对整个模型模型进行拓展。甚至,可以开发出自己的验证授权系统。

  最后附上两个文章,是flask系统中如何自己编写权限认证,如果你熟悉django的权限认证系统,完全可以自己写出一个来使用,使用轮子是为了能造出更好的轮子:

  Flask权限篇flask_principal

  flask权限管理

8.补充

django的permission是表级别的权限,如果在某些特殊情况下,需要执行行级别的数据,咋可以使用django-guardian,参照下面博客:

https://blog.csdn.net/scdxmoe/article/details/72678005

  

猜你喜欢

转载自www.cnblogs.com/znn041622/p/10991598.html