Django之auth认证系统

目录

一 、auth模块
二 、User对象
三 、扩展默认的auth_user表

一.auth模块

在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,真是个麻烦的事情。
Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统–auth,它默认使用 auth_user 表来存储用户数据,使用auth模块来进行用户认证,那么需要使用人家django自带的auth_user表来存储用户的信息数据。
导入模块:

from django.contrib import auth

可以通过python manage.py createsuperuser来给auth_user表添加数据,创建一个超级管理员:

python manage.py createsuperuser

Django之auth认证系统_第1张图片
数据库中,使用createsuperuser创建出来的用户密码会加密,这里下面会写到:
在这里插入图片描述
内置的User模型拥有以下的字段:

username: 用户名。150个字符以内。可以包含数字和英文字符,以及_、@、+、.和-字符。不能为空,且必须唯一!
first_name:歪果仁的first_name,在30个字符以内。可以为空。
last_name:歪果仁的last_name,在150个字符以内。可以为空。
email:邮箱。可以为空。
password:密码。经过哈希过后的密码。
#groups:分组。一个用户可以属于多个分组,一个分组可以拥有多个用户。groups这个字段是跟Group的一个多对多的关系。
#user_permissions:权限。一个用户可以拥有多个权限,一个权限可以被多个用户所有用。和 	    Permission属于一种多对多的关系。
is_staff:是否可以进入到admin的站点。代表是否是员工。这个字段如果不使用admin的话,可以自行忽略,不影响使用
is_active:是否是可用的。对于一些想要删除账号的数据,我们设置这个值为False就可以了,而不是真正的从数据库中删除。
is_superuser:是否是超级管理员。如果是超级管理员,那么拥有整个网站的所有权限。
last_login:上次登录的时间。
date_joined:账号创建的时间。

但是源码中可以看到AbstractUser是继承了AbstractBaseUser和PermissionsMixin:
在这里插入图片描述
那么AbstractUser和AbstractBaseUser到底有什么区别呢?

  • 如果你对django自带的User model刚到满意, 又希望额外的field的话, 你可以扩展AbstractUser类
  • AbstractBaseUser中只含有3个字段: password, last_login和is_active,如果你对默认的auth_user中有不满意的地方或者不想要的字段, 或者只想保留默认的密码储存方式, 则可以选择这一方式.

AbstractBaseUser中提供了一系列的关于密码校验以及邮箱校验的方法:
而在AbstractUser中则没有这些方法。

def save(self, *args, **kwargs):
    super().save(*args, **kwargs)
    if self._password is not None:
        password_validation.password_changed(self._password, self)
        self._password = None

def get_username(self):
    """Return the username for this User."""
    return getattr(self, self.USERNAME_FIELD)

def clean(self):
    setattr(self, self.USERNAME_FIELD, self.normalize_username(self.get_username()))

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

# 是否是匿名用户
@property
def is_anonymous(self):
    """
    Always return False. This is a way of comparing User objects to
    anonymous users.
    """
    return False

# 身份认证
@property
def is_authenticated(self):
    """
    Always return True. This is a way to tell if the user has been
    authenticated in templates.
    """
    return True

# 通过createsuperuser创建出来的用户密码是用set_password进行哈希加密的
def set_password(self, raw_password):
    self.password = make_password(raw_password)
    self._password = raw_password

# 检查密码
def check_password(self, raw_password):
    """
    Return a boolean of whether the raw_password was correct. Handles
    hashing formats behind the scenes.
    """
    def setter(raw_password):
        self.set_password(raw_password)
        # Password hash upgrades shouldn't be considered password changes.
        self._password = None
        self.save(update_fields=["password"])
    return check_password(raw_password, self.password, setter)

def set_unusable_password(self):
    # Set a value that will never be a valid hash
    self.password = make_password(None)

def has_usable_password(self):
    """
    Return False if set_unusable_password() has been called for this user.
    """
    return is_password_usable(self.password)

def get_session_auth_hash(self):
    """
    Return an HMAC of the password field.
    """
    key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"
    return salted_hmac(key_salt, self.password).hexdigest()

@classmethod
def get_email_field_name(cls):
    try:
        return cls.EMAIL_FIELD
    except AttributeError:
        return 'email'

@classmethod
def normalize_username(cls, username):
    return unicodedata.normalize('NFKC', username) if isinstance(username, str) else username

AbstractBaseUser和AbstractUser总结:

AbstractUser是一个完整的用户模型,带有字段,作为抽象类,因此您可以从中继承并添加自己的配置
字段和方法。AbstractBaseUser仅包含身份验证功能,。如果您想重新考虑Django有关身份验证的一
些假设,那么AbstractBaseUser可以为您提供这样做的能力。

auth中提供了许多实用方法:

authenticate():
导入模块:from django.contrib.auth import authenticate
提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数,因为你仔细看看auth_user表的话,你会发现用户名和密码的字段名称就是username和password。

如果认证成功(用户名和密码正确有效,就是去auth_user表中查询一下是否存在这条记录),便会返回一个 User 对象,查询认证失败返回None。

authenticate()会在该 User 对象上设置一个属性来标识后端已经认证了该用户,且该信息在后续的登录过程中是需要的。

示例:

user = auth.authenticate(username='xx',password='123456')

用法:

def query(request):
    if request.method == 'POST':
        user = authenticate(request,**json.loads(request.body))
        if not user:
            return HttpResponse('用户名或者密码错误!')
        else:
            return HttpResponse('ok')

结果:
Django之auth认证系统_第2张图片

login(HttpRequest, user):
该函数接受一个HttpRequest对象,以及一个经过认证的User对象。

该函数实现一个用户登录的功能。它本质上会在后端为该用户生成相关session数据,保持会话用。

def query(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_date = {
     'username': username, 'password': password}
        user_obj = authenticate(request, **user_date)
        if not user_obj:
            return HttpResponse('用户名或者密码错误!')
        else:
        可以简单理解为request.session['user_id']=user_id,并且将user_obj封装到了
        request里面,通过request.user= user_obj
            login(request, user_obj) 
            return redirect('index')


def index(request):
    return render(request, 'index.html')

保存一个sessionid:
Django之auth认证系统_第3张图片
这里从AuthenticationMiddleware这个中间件点击进去的确可以看到是封装了一个reques.user对象,后续我们可以通过reques.user这个对象来获取user模型中的所有属性,具体源码后续是怎么实现的,有兴趣的可以自己看一下。
Django之auth认证系统_第4张图片
如果没有登录的情况下使用request.user呢?那么将返回一个AnonymousUser:

def index(request):
    print(request.user)  # 显示的是一个匿名用户
    print(request.user.id)
    print(request.user.username)
    print(request.user.is_active)
    return render(request, 'index.html')

结果:
Django之auth认证系统_第5张图片
登录后的:
在这里插入图片描述
注意的点:
只要使用login(request, user_obj)之后,request.user就能拿到当前登录的用户对象。否则request.user得到的是一个匿名用户对象(AnonymousUser Object,是request.user的默认值)。

request.user一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。

如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 
的一个实例。你可以通过 is_authenticated() 区分它们。

例如:

user = authenticate(request, **request.json)
    if not user:
        return api.bad_request()

    auth_login(request, user)
    return api.ok(user, group='userinfo')

另外注意:
user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。

get_user源码中返回的是user或者是AnonymousUser()实例,也就是说user为真则返回user对象,反之返回一个AnonymousUser的实例:
Django之auth认证系统_第6张图片
AnonymousUser,匿名用户所有字段都为假,表示无任何权限:
Django之auth认证系统_第7张图片

id 永远为None。
username 永远为空字符串。
get_username() 永远返回空字符串。
is_staff 和 is_superuser 永远为False。
is_active 永远为 False。
groups 和 user_permissions 永远为空。
is_anonymous() 返回True 而不是False。
is_authenticated() 返回False 而不是True。
set_password()、check_password()、save() 和delete() 引发 NotImplementedError。

在真实项目中如果没有登录但操作request.user则会触发AnonymousUser异常,根据操作不同异常可能会不相同:
Django之auth认证系统_第8张图片
logout(request):

该函数接受一个HttpRequest对象,无返回值。

当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。
示例:

def api_logout(request):
    logout(request)
    return redirect('login')

Django之auth认证系统_第9张图片

二.User对象

user对象的 is_authenticated():

Django使用session和中间件关联请求对象中和认证系统。

认证用户:
每一次请求中都包含一个request.user属性,表示当前用户。如果该用户未登陆,该属性的值是一个AnonymousUser实例(匿名用户),如果已经登录,该属性就是一个User模型的实例。
注意:
通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name。

可以使用is_authenticated方法进行判断,如下:


def admin_required(viewfunc):
    @wraps(viewfunc)
    def wrapper(request, *args, **kwargs):
		
		# 是否登录
        if not request.user.is_authenticated:
            return api.not_authorized()
		
		# 是否是管理员
        if not request.user.is_admin:
            return api.forbidden(message='需要管理员用户')

        return viewfunc(request, *args, **kwargs)

    return wrapper

后续直接将该装饰器放在视图函数中即可,使用方法如下:

from django.utils.decorators import method_decorator
@method_decorator(admin_required, name='dispatch')
class UserView(View):

    def get(self, request, id=None):
        if id is not None:
            return self._get_user(request, id)
        else:
            return self._list_user(request)

示例,可以在index页面判断用户是否登录:
方法一:

def index(request):
    if not request.user.is_authenticated:
        return redirect('login')
    else:
        return render(request, 'index.html')

方法二:
login_requierd():
django提供了用于此种情况的装饰器:login_requierd()。

  • 如果用户未登陆,重定向到settings.LOGIN_URL,传递当前绝对路径作为url字符串的参数,例如:/login/
  • 如果用户已经登录,执行正常的视图

首先在settings.py文件中设置如下内容:

LOGIN_URL = '/login/'  # 需要重定向到的路径,一定是存在的

视图函数:

@login_required()
def index(request):
    if not request.user.is_authenticated:
        return redirect('login')
    else:
        return render(request, 'index.html')

这里url路径前面表示我们自定义的重定向路径,后面表示登录成功后要进入的页面:
Django之auth认证系统_第10张图片
这里需要注意is_authenticated和authenticated的区别:

authenticated:authenticate 就是身份验证,举个例子,比如你提供了账号、密码,
传给 authenticate 他就会检查你提供的信息是否正确,正确了就会返回 user。

is_authenticated:验证用户是否登录,在每个Web请求中都提供一个 request.user 
属性来表示当前用户。如果当前用户未登录,则该属性为AnonymousUser的一个实例,
反之,则是一个User实例。

create_user():
封装在一个UserManager管理器中,用于创建用户:
示例:

def create(request):
    User.objects.create_user(username='张三',password='123')
    return HttpResponse('ok')

create_superuser():
auth 提供的一个创建新的超级用户的方法,需要提供必要参数(username、password)等。

def create(request):
    User.objects.create_superuser(username='李四',password='456')
    return HttpResponse('ok')

check_password(raw_password):
检查密码是否正确的方法,需要提供当前请求用户的密码。密码正确返回True,否则返回False。

可直接打印:
request.user.check_password(password)

set_password(raw_password):
修改密码的方法,接收 要设置的新密码 作为参数。
注意:设置完一定要调用用户对象的save方法!!!

request.user.set_password('新密码')
request.user.save()        

request.user能够拿到认证所用用户表的数据属性,比如username, password等。

  • reques.user.is_active :是否允许用户登录
  • is_staff : 用户是否拥有网站的管理权限.
  • has_permission:是否有权限

三.扩展默认的auth_user模型

auth_user表字段都是固定的那几个,我在项目中没法拿来直接使用啊,如果我要添加几个额外的字段怎么办?
答案是当然有了。
可以通过继承内置的 AbstractUser 或者AbstractBaseUser类(两者区别上述已经介绍),来定义一个自己的Model类。如果没有继承AbstractUser,那么makemigrateions和migrate的时候则会创建auth_user这张表,如果继承了AbstractUser则只会创建自己的user表,继承AbstractBaseUser则会创建auth_user表(这里有待商榷,本人未去尝试,但在我做的项目中是有这个auth_user表的)。

这样既能根据项目需求灵活的设计用户表,又能使用Django强大的认证系统了。继承表的好处是我们可以增加一些自己需要的字段,并且同时可以使用auth模块提供的接口、方法。
示例:

from django.contrib.auth.models import AbstractBaseUser,AbstractUser

class User(AbstractUser):
    phone = models.CharField(max_length=11, null=True, unique=True)

注意:
User表里就不需要有auth_user里重复的字段了,比如说username以及password等,但是还是可以直接使用这些字段的,并且django会自动将password进行加密。

扩展了内置的auth_user表之后,一定要在settings.py中告诉Django,我现在使用我新定义的User表来做用户认证。写法如下:

AUTH_USER_MODEL = "manytable.User"

创建完成之后不要忘了makemigrations和migrate。
后续就可以像使用默认的auth_user表那样使用我们的User表了。比如:

User.objects.create_superuser(username='李四',password='456')
User.objects.create_user(username='李四',password='456')

如有异议或者不明白不理解的欢迎加群交流:934244622

你可能感兴趣的:(python,django,orm,django,python,orm,数据库,后端)