Django实现自定义用户验证

强调:Django版本:1.9.5,Python版本:3.5
很多时候我们都要为一个系统开发用户验证的功能,而Django就为我们提供了一套现成的用户验证方法。
Django的用户模型在contrib.auth.models.py,如下:

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'

查看AbstractUser的源码,我们可以看到这个用户实体具有如下属性:
username、first_name、last_name、email、is_staff、is_active、date_joined
除此之外,AbstractUser还继承AbstractBaseUserPermissionsMixin
AbstractBaseUser的属性有:password、last_login
PermissionsMixin用于权限控制。
当新建了一个Django项目,并且配置了数据库后,如果运行如下命令:

python manage.py migrate

就可以在数据库中看到生成了几张表,其中auth_user即为用户实体对应的表。
既然用户实体有了,那怎么进行用户验证呢。相关源码在django.contrib.auth.backends.ModelBackend
ModelBackend中,有一个叫authenticate的方法,它就是验证逻辑的核心:

 def authenticate(self, username=None, password=None, **kwargs):
     UserModel = get_user_model()
     if username is None:
         username = kwargs.get(UserModel.USERNAME_FIELD)
     try:
         user = UserModel._default_manager.get_by_natural_key(username)
         if user.check_password(password):
             return user
     except UserModel.DoesNotExist:
         # Run the default password hasher once to reduce the timing
         # difference between an existing and a non-existing user (#20760).
         UserModel().set_password(password)

可以看出,如果用户不存在,那么Django会为这个用户设置当前输入的密码。
这个方法的使用方式如下:

from django.contrib import auth
user = auth.authenticate(username='xxx', password='xxx')
if user is not None:
    auth.login(request, user)

调用auth.login的目的是为这个用户设置一些session信息,具体可参考源码。
当用户退出登录时,需要调用如下方法清除session:

auth.logout(request)

很多时候我们的用户实体的属性可能不仅仅是这些,又或者不仅仅验证用户名和密码是否正确。
这个时候我们就可以自定义用户模型和验证逻辑,自定义用户模型可以继承Django的AbstractBaseUser
自定义验证逻辑,除了可以继承ModelBackend并重写authenticate方法之外,也可以写一个普通类,但是其中至少包含authenticateget_user两个方法。
当完成以上两个类后,就需要在配置文件settings.py中引入它们:

AUTH_USER_MODEL = 'user.CustomUser'

AUTHENTICATION_BACKENDS = [
    'user.views.MyBackend'
]

AUTHENTICATION_BACKENDS配置项可以包含多个backend,Django将从上到下执行,一旦用户验证通过,将不再执行后面的验证方法。如果在这过程产生PermissionDenied异常,验证也将立马终止。这个逻辑的相关源码在auth.authenticate,如下:

def authenticate(**credentials):
    """
    If the given credentials are valid, return a User object.
    """
    for backend, backend_path in _get_backends(return_tuples=True):
        try:
            inspect.getcallargs(backend.authenticate, **credentials)
        except TypeError:
            # This backend doesn't accept these credentials as arguments. Try the next one.
            continue

        try:
            user = backend.authenticate(**credentials)
        except PermissionDenied:
            # This backend says to stop in our tracks - this user should not be allowed in at all.
            return None
        if user is None:
            continue
        # Annotate the user object with the path of the backend.
        user.backend = backend_path
        return user

    # The credentials supplied are invalid to all backends, fire signal
    user_login_failed.send(sender=__name__,
            credentials=_clean_credentials(credentials))

其中,inspect.getcallargs(backend.authenticate, **credentials)是用于判断当前backend是否接受传进来的参数,如果不接受,将继续寻找下一个backend。

猜你喜欢

转载自blog.csdn.net/baidu_30526767/article/details/85015238
今日推荐