# 源码位于 django/contrib/auth/__init__.py defauthenticate(request=None, **credentials): """ If the given credentials are valid, return a User object. """ # 通过 _get_backends 读取settings.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: # 调用获取到的认证类的 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 # 如果返回的值是None(认证函数没有返回值时,得到的是None,或者有的认证函数就是返回的None)的话,说明当前调用的认证类认证失败,继续尝试下一个 if user isNone: continue # 会为用户对象添加backend参数,用来记录认证时使用的认证类 # 该参数会在login()方法中,放到session中保存 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)
# 默认调用的认证类 ModelBackend # 源码位于 django/contrib/auth/backends.py classModelBackend: # 提供认证方法authenticate() defauthenticate(self, request, username=None, password=None, **kwargs): # 内部主要通过传递过来的用户名和密码完成身份的验证 if username isNone: username = kwargs.get(UserModel.USERNAME_FIELD) try: 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: if user.check_password(password) and self.user_can_authenticate(user): return user
# 源码位置 django/contrib/auth/__init__.py deflogin(request, user, backend=None): """ Persist a user id and a backend in the request. This way a user doesn't have to reauthenticate on every request. Note that data set during the anonymous session is retained when the user logs in. """ session_auth_hash = '' if user isNone: # 如果传递的user参数是None的话,会自动赋值当前request.user中的值,可能是匿名用户AnonymousUser的实例对象 user = request.user # 由中间件AuthenticationMiddleware赋予,可能是存在的用户,也可能是匿名用户AnonymousUser ifhasattr(user, 'get_session_auth_hash'): # get_session_auth_hash()是AbstractBaseUser类特有的方法,该类是django中用户表的最上层基础类,所以若是用户表中的对象就一定可以调用该方法 session_auth_hash = user.get_session_auth_hash() # 返回密码字段的HMAC值
if SESSION_KEY in request.session: # _auth_user_id,request.session由中间件SessionMiddleware赋予 if _get_user_session_key(request) != user.pk or ( session_auth_hash and not constant_time_compare(request.session.get(HASH_SESSION_KEY, ''), session_auth_hash)): # To avoid reusing another user's session, create a new, empty # session if the existing session corresponds to a different # authenticated user. request.session.flush() else: request.session.cycle_key()
try: # or 运算符,a or b, 如果a为真,表达式为a,反之,为b # 所以在未传递该参数的情况下,当前表达式的值为user.backend backend = backend or user.backend except AttributeError: # 异常只有一种情况,就是使用user.backend时,user没有backend属性,该属性是在使用authenticate()认证之后赋予的,为 backend_path 的值 # 此时用户可能是AnonymousUser(AnonymousUser对象没有该属性),也可能是直接从数据库取的未认证的对象(认证过的对象会添加backend参数) # 此时就手动调用获得设置的 认证类,但是此时需要配置中只能有一个认证的类,否则报异常(理所当然,如果允许多个认证类,登录认证就没有意义了) backends = _get_backends(return_tuples=True) iflen(backends) == 1: _, backend = backends[0] else: raise ValueError( 'You have multiple authentication backends configured and ' 'therefore must provide the `backend` argument or set the ' '`backend` attribute on the user.' ) else: ifnotisinstance(backend, str): raise TypeError('backend must be a dotted import path string (got %r).' % backend)