Django使用AUTHENTICATION_BACKENDS指定授权的后端

本文深入探讨了Django框架中的用户认证机制,特别是authenticate和get_user方法的使用与自定义过程,解析了这些方法如何与Django的认证系统交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于这块内容,建议先阅读下官网说明,着重看编写一个验证后端板块,再看下我的困惑。

编写一个验证后端的示例代码如下:

from django.conf import settings
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User

class SettingsBackend:
    """
    Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

    Use the login name and a hash of the password. For example:

    ADMIN_LOGIN = 'admin'
    ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
    """

    def authenticate(self, request, username=None, password=None):
        login_valid = (settings.ADMIN_LOGIN == username)
        pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
        if login_valid and pwd_valid:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                # Create a new user. There's no need to set a password
                # because only the password from settings.py is checked.
                user = User(username=username)
                user.is_staff = True
                user.is_superuser = True
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

关于authenticate和get_user方法,文档中这么说道:

"get_user"方法的参数是"user_id",并返回一个user对象或None。参数user_id也可能是username、数据库id,或者其他值,但这个参数必须你的user对象的主键。authenticate``方法接受 ``request 参数和 credentials 关键字参数,大多数情况下,该方法类似于下面的代码:

class MyBackend:
    def authenticate(self, request, username=None, password=None):
        # Check the username/password and return a user.
        ...

但它也可能验证一个Token,就像这样:

class MyBackend:
    def authenticate(self, request, token=None):
        # Check the token and return a user.
        ...

无论是哪一种方式,authenticate()``都应该检查所获得的凭证,并当凭证有效时返回一个用户对象。当凭证无效时,应该返回``None

request 是 HttpRequest ,默认为 None 如果没有被提供给 authenticate() (它把request传给后端).

最开始的时候,我是这么想的,既然是Django框架内部完成调用的,那么我们只要按照这个格式去做对应的实现就好了,也没什么困惑的地方;然而在实际开发中,我在一些项目里看到authenticate和get_user方法的参数可以不是上述提到的,例如我可以写hq_passport、hq_uuid等等。那么问题就来了,方法的参数声明定义难道没有限制? 可以随便填写? 那么Django实际在调用这部分代码的时候,到底是怎么传参给它的呢? 基于这个困惑,我问了下度娘,没得到我想要的结果,没办法,只能去阅读源码了,也写篇博文分享下自己的观点。

小剧场:在我们使用Django进行web开发的时候,无可避免的需要考虑用户登录授权问题,Django作为一个完美主义者的终极框架,当然也考虑到了。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。所以一般我们都会直接使用Django自带的这部分。其实我要说的内容(authenticate和get_user)部分,也是其中的一环节。要说明的一点是,我们在settings.py文件里虽然配置了AUTHENTICATION_BACKENDS配置,但这并不代表,服务启动了,它就被自动调用了。只有在需要登录的时候,这个配置才会生效。例如Django的Admin管理后台登录是,也会调用这个的,代码在site-package的django/contrib/auth/forms.py的clean方法(读取用户输入的表单数据时),代码如下:

def clean(self):
    username = self.cleaned_data.get('username')
    password = self.cleaned_data.get('password')

    if username is not None and password:
        self.user_cache = authenticate(self.request, username=username, password=password)
        if self.user_cache is None:
            raise forms.ValidationError(
                self.error_messages['invalid_login'],
                code='invalid_login',
                params={'username': self.username_field.verbose_name},
            )
        else:
            self.confirm_login_allowed(self.user_cache)

    return self.cleaned_data

经验证,如果你不使用auth的认证特性,或者说你不手动调用使用这个配置的话,你的这个配置实际上也是不会生效的。在基于Django的Auth模块实现用户登录认证时,到底怎么调用呢,下面放上一段样例代码:

from django.contrib.auth import authenticate, login, logout

def login(request):
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        login(request, user)
        # Redirect to a success page.
        ...
    else:
    # Return an 'invalid login' error message.
        ...

def logout_view(request):
    logout(request)
    # Redirect to a success page.

这段代码,通常是后端提供给前端的登录认证接口。我们来看看django.contrib.auth.anthenticate函数源码,_get_backends方法会去读取AUTHENTICATION_BACKENDS配置,然后一步步做认证授权,直到返回具体的user:

def authenticate(request=None, **credentials):
    """
    If the given credentials are valid, return a User object.
    """
    for backend, backend_path in _get_backends(return_tuples=True):
        try:
            user = _authenticate_with_backend(backend, backend_path, request, credentials)
        except PermissionDenied:
            # This backend says to stop in our tracks - this user should not be allowed in at all.
            break
        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), request=request)

也就是说,authenticate函数的调用是开发人员操作的,所以我们在自定义授权的后端时,authenticate函数的参数可以根据需要自行声明。get_user函数也是类似的逻辑,这里就不说了,有兴趣的童鞋,可以自行看看源码。当然了,要是觉得django自带的用户登录认证授权不好,大佬们也可以自实现。欢迎批评指正,一起学习交流。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值