关于这块内容,建议先阅读下官网说明,着重看编写一个验证后端板块,再看下我的困惑。
编写一个验证后端的示例代码如下:
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自带的用户登录认证授权不好,大佬们也可以自实现。欢迎批评指正,一起学习交流。