django 有自己的认证方法,不过是基于用户名密码(根据自然键 USERNAME_FIELD
的方式,实现简单的认证方式,如下:
from django.contrib.auth import login as auth_login
def login(request):
# 最主要在 authenticate 里面
user = authenticate(request, **request.POST)
if not user:
return JsonResponse({"message":"登录失败"})
auth_login(request, user)
return JsonResponse({"message": "success"})
循环每一个 backend,执行里面的 authenticate 方法,该方法一定要实现。
def authenticate(request=None, **credentials):
"""
If the given credentials are valid, return a User object.
"""
# 循环每一个认证方式,返回一个认证好的 user 对象
# _get_backends,是 settings.py 中AUTHENTICATION_BACKENDS 列表里面定义的认证方式
# 如果没有定义就用默认的认证方式
for backend, backend_path in _get_backends(return_tuples=True):
try:
inspect.getcallargs(backend.authenticate, request, **credentials)
except TypeError:
# This backend doesn't accept these credentials as arguments. Try the next one.
continue
try:
# 不管是内置的还是自定义的,每个 backend 都要实现一个 authenticate
# authenticate 里面是自己编写好的认证逻辑
user = backend.authenticate(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)
默认的认证方式:
没有定义 AUTHENTICATION_BACKENDS ,那么去哪里找它用了那个 backend 呢,可以去全局去找,里面肯定有默认的 backend。
from django.conf import settings
global_settings.py
AUTH_USER_MODEL = 'auth.User'
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
ModelBackend:
导入它,进去看:from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.backends import ModelBackend
class ModelBackend:
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
# 通过自然键(USERNAME_FIELD)获取 user 对象
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
# 检查密码和是否是活跃用户,然后返回一个 user 对象,否则什么也不返回 就是 None
if user.check_password(password) and self.user_can_authenticate(user):
return user
为什么自定义 backend, 这里只支持用户名(根据 USERNAME_FIELD 设置的字段) 来认证,如果有其他的方式 django 是没办法支持,比如验证码登录、微信登录、token登录(drf支持)、LDAP登录,这些不太好支持,需要自定义一些认证来实现。
from django.contrib.auth.backends import BaseBackend
""""
一般要继承 BaseBackend 然后实现 authenticate,但 django 4.0 以前是不用的,
就是可以不用继承 BaseBackend,直接实现就行,或者继承 ModeBackend, 覆盖里面的
authenticate 和 user_can_authenticate,根据自己需求。
"""
class PasswordBackend(BaseBackend):
def authenticate(self, request, method, **kwargs):
# 密码登录
if method != 'password':
return None
class OtpCodeBackend(BaseBackend):
def authenticate(self, request, method, **kwargs):
# 短信验证码登录
if method != 'otpcode':
return None
class WechatBackend(BackendBase):
def authenticate(self, request, method, **kwargs):
if method != 'wechat':
return None
settings.py 中:
AUTHENTICATION_BACKENDS = [
# 自己自定义 backend 的路径
'app01.account.backends.PasswordBackend',
'app01.account.backends.OtpCodeBackend',
'app01.account.backends.WechatBackend',
]
django 官网:https://docs.djangoproject.com/zh-hans/4.0/topics/auth/customizing/
django 自带的 auth 认证系统:https://blog.csdn.net/qq_39253370/article/details/108464419?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165460504816781818740619%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165460504816781818740619&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-1-108464419-null-null.article_score_rank_blog&utm_term=auth&spm=1018.2226.3001.4450