DjangoLearning_charter11验证模型User及其拓展方式

验证和授权系统

验证和授权系统简介

 

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

 

1、包括以下几个方面:

用户

权限

分组

一个可以配置的密码哈希系统

一个可插拔(可用可删, 删除不会对项目产生非常大的影响)的后台管理系统

 

2、使用授权系统:

  • INSTALLED_APPS:

  1. Django.contrib.auth: 包含一个核心授权框架, 以及大部分的模型定义
  2. Django.contrib.contenttypes: content type 系统, 可以用来关联模型和权限
  • 中间件:

  1. SessionMiddleware: 用来管理session
  2. AuthticationMiddleware: 用来处理和当前session相关联的用户

内置User模型使用

User模型是这框架的核心部分。 他的完整路径是django.contrib.auth.models.User

1、 字段解释:

  1. username:用户名, 150个字符以内, 可以包含_  @ + . – 等字符。 不能为空, 且必须唯一
  2. first_name:外国人的first_name, 在30个字符以内, 可以为空
  3. last_name: 外国人的last_name, 在150个字符以内, 可以为空
  4. email:邮箱,可以为空
  5. password:密码,经过哈希的密码
  6. groups:分组, 一个用户可以属于多个分组, 一个分组可以有多个用户。属于多对多关系
  7. user_permissions:权限, 也是多对多关系
  8. is_staff:是否可以进入到admin站点, 代表是否是员工
  9. is_active:是否是可用的, 对于删除账号的数据, 并不是删除数据, 而是将这个值设置为False
  10. is_superuser:是否是终极管理员,拥有网站的所有权限,且不受权限函数等的限制。
  11. last_login:上次登录时间
  12. first_login:账号创建时间

2、 User模型的基本用法

  1. 创建用户:

    通过create_user进行创建,必须传入username,email,password

from django.contrib.auth.models import User
def index(request):
    user = User.objects.create_user('piggy', 'piggy@qq.com', '123456')
    # 此时user已经保存到数据库, 也可以另外做其他操作
    user.last_name = 'shi'
    user.save()
    return HttpResponse('success')

 

    创建完成的用户存储在auth_user表之中, 对密码做了加密处理

  2. 创建超级用户:

      使用User创建

User.objects.create_superuser(username='piggy_super',
email='super@qq.com', password='123456')

    使用命令行的方式创建

python manage.py createsuperuser

 

 

  3. 修改密码:

    因为密码是加密之后存储的, 因此修改密码不能直接用user.password = “”·

user = User.objects.get(pk=1)
user.set_password('333333')
user.save()

  4. 登录验证:

user = authenticate(request, username='piggy', password='123456')
if user:
    return HttpResponse('登陆成功')
else:
    return HttpResponse('Y用户名或密码错误')

  5.User模型的缺点

  1. 没有手机号码字段
  2. 验证的时候是用用户名和密码, 但是在我国通常使用邮箱或者手机号码

 

拓展User模型

1、Proxy模型:

可插拔、无污染的代理模型,不会对user模型产生任何的模型, 但是不能扩展模型

模型:

from django.contrib.auth.models import User

class Person(User):
class Meta:
        proxy = True

    @ classmethod
    def get_blacklist(cls):
        return cls.objects.filter(is_active=False)

视图函数:

def proxy_view(request):
    for i in Person.get_blacklist():
        print(i.username, type(i.username))
        print(i, type(i))
    return HttpResponse('success')

>> piggy <class 'str'>
>> piggy <class 'front.models.Person'>

注意点: User.objects.all()  Person.objects.all()完全等价

缺点:代理模型是不能用models添加字段的, 新的 fields。 但是可以添加新的属性

 

 

2、一对一外键

如果你对用户验证方法authenticate没有其他要求, 就是使用username和password完成, 但是想要在原来的模型之上增加新字段, 那么可以用一对一外键的形式

  • 用外键的方式创建模型:
class UserExtension(models.Model):

    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='extension')
    telephone = models.CharField(max_length=11)
    school = models.CharField(max_length=100)

 

  • 在models内接收信号:
from django.dispatch import receiver
from django.db.models.signals import post_save
@ receiver(post_save, sender=User)
def handler_user_extension(sender, instance, created, **kwargs):
    if created:
        UserExtension.objects.create(user=instance)
    else:
        instance.extension.save()
  • 视图函数(还是在User内创建用户, 必须用create_user, 否则密码不加密):
def one_view(request):
    User.objects.create_user(username='piggy_shi', password='132465', email='shi@qq.com')
    return HttpResponse('一对一扩展User模型')
  • 此时会在一对一外键表内生成一条对应数据

 

那么, 如果想用手机号去验证, 如何操作呢?


 

修改验证方式:

  • 首先是需要写新的验证函数

注意点: 手机号码是通过UserExtension模型通过外键关联到user上的, 因此不能直接访问telephone,而需要使用模型定义时的related_name + 双下划线+ 字段名 的形式, 即  extension__telephone

def my_authenticate(telephone, password):
    user = User.objects.filter(extension__telephone=telephone).first()
    if user:
        if user.check_password(password):
            return user
        else:
            return None
    else:
        return None

 

  • 其次需要往数据库里存入一条有手机号的用户信息

注意点:给user指定手机号时, 也需要通过extension调用,而不能直接user.telephone进行指定,原理与上面一致

user = User.objects.create_user(username='piggy_shi4',
password='111111', email='shi@qq.com')
user.extension.telephone = '11111111111'
user.save()
  • 最后进行验证测试
telephone = request.GET.get('telephone')
password = request.GET.get('password')
user = my_authenticate(telephone, password)
if user:
    print('验证成功')
else:
    print('验证失败')
  • 实现手机号码和密码验证登录

 

 


 

继承AbstractUser

创建模型:

  • 命名为User
  • 继承django默认User的父类AbstractUser(from django.contrib.auth.models import AbstractUser)
  • USERNAME_FIELD 代表进行验证字段(原始User默认是username字段)
  • 如果想实现通过手机号验证,必须重写验证方法, 然后在User中 传入参数 objects = 类(注意: 必须加括号, 否则只是传入类名, 而没有实例化,示例:objects = UserManager()
class User(AbstractUser):
telephone = models.CharField(max_length=11, unique=True)
school = models.CharField(max_length=100)

USERNAME_FIELD = 'telephone'
objects = UserManager()

安装APP、创建模型与项目连接

  • 必须在settings中创建AUTH_USER_MODEL变量,否则会报如下错
  • APP后不需要跟models, 直接跟模型名即可
  • AUTH_USER_MODEL代表的字段必须为唯一, 即传入unique=True

 

 

 

AUTH_USER_MODEL = 'front.User'

重写objects方法

  • objects是复制了BaseUserManager 的所有属性和方法
  • 方法前加一个下划线表示只能在类内访问
  • self.model代表当前模型, 即User
  • create方法必须返回值, 否则会在视图函数报错
  • user的set_password是方法, 因此是传入参数而不是指定值为password, 否则用密码值代替原始方法,且用户在数据库的密码为空
class UserManager(BaseUserManager):
def _create_user(self, telephone, password, username=None, **kwargs):
if not telephone or not password:
raise ValueError('The given telephone or password must be set')
user = self.model(telephone=telephone, username=username, **kwargs)
user.set_password(password)
# user.set_password = password # 错误的使用方式
user.save()
return user

def create_user(self, telephone, password, username=None, **kwargs):
kwargs['is_superuser'] = False
return self._create_user(telephone=telephone, password=password, username=username, **kwargs)

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

视图函数:

def inherit_view(request):
    telephone = '11111111111'
    password = '111111'
    username = 'piggy'
    user = User.objects.create_user(telephone=telephone, password=password, username=username)
    print(user.username)
    return HttpResponse('继承')

 

验证:

from django.contrib.auth import authenticate
def authenticate_view(request):
user = authenticate(request, username='18888888888', password='111111')
if user:
print('验证成功')
else:
print('验证失败')
return HttpResponse('验证界面')

 


 

继承AbstractBaseUser

场景:想要修改默认的验证方式, 且有些user默认字段不想要(比如first_name,last_name),可以使用此方法创建类

  • 继承AbstractBaseUser。但是最好确保对django有一定了解, 因为要尽可能的模拟user模型,否则可能出现隐患
  • 继承PermissionsMixin, 用于用户权限
  • AbstractBaseUser只包含了password加密算法和is_active,last_login记录

 

 创建模型

继承AbstractBaseUser和PermissionsMixin

USERNAME_FIELD:用于验证的字段

REQUIRED_FIELD: 用命令行创建超级管理员时需要输入的字段, 如果是空列表,默认是需传USERNAME_FIELD和password

objects:模型的objects属性

尽量模拟AbstractUser的写法,防止出现未知错误

class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=100, unique=True)
    telephone = models.CharField(max_length=11, unique=True)
    email = models.EmailField()
    is_active = models.BooleanField(default=0)

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = []
    objects = UserManager()

    def get_full_name(self):
        return self.username

    def get_short_name(self):
        return self.username

 

实现UserManager

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin


class UserManager(BaseUserManager):

    def _create_user(self, telephone, email, password, username,  **kwargs):
        if not telephone or not password:
            raise ValueError('The given telephone or email must be set')
        user = self.model(telephone=telephone, email=email, username=username, **kwargs)
        user.set_password(password)
        user.save()
        return user

    def create_user(self, telephone, email, password, username, **kwargs):
        kwargs['is_superuser'] = False
        return self._create_user(telephone=telephone, email=email, password=password, username=username, **kwargs)

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

 

 验证

from django.contrib.auth import authenticate

def authenticate_view(request):
    user = authenticate(request, username='18888888899', password='123456')
    if user:
        print(user.username)
    else:
        print('失败')
    # user = User.objects.filter(telephone='18888888899').first()
    # print(user.check_password('123456'))
    return HttpResponse('验证界面')

 

posted @ 2020-03-27 15:30  扛大炮的  阅读(137)  评论(0)    收藏  举报