django.contirb.auth-认证

本文深入解析Django的认证模块工作原理,包括中间件如何在请求中增加用户属性、session存储机制、用户对象属性及获取过程等,并介绍了典型登录流程。

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

摘要  django自带了一个认证模块,它利用middlerware在request里增加了user属性,用来获取认证用户的信息。认证后台采用session存储。它自带的User模型,也可以支持自定义扩展。

首先看middleware的定义:

auth模块有两个middleware:AuthenticationMiddleware和SessionAuthenticationMiddleware。

AuthenticationMiddleware负责向request添加user属性

?
1
2
3
4
5
6
7
8
9
class  AuthenticationMiddleware( object ):
     def  process_request( self , request):
         assert  hasattr (request,  'session' ), (
             "The Django authentication middleware requires session middleware "
             "to be installed. Edit your MIDDLEWARE_CLASSES setting to insert "
             "'django.contrib.sessions.middleware.SessionMiddleware' before "
             "'django.contrib.auth.middleware.AuthenticationMiddleware'."
         )
         request.user  =  SimpleLazyObject( lambda : get_user(request))

可以看见AuthenticationMiddleware首先检查是否由session属性,因为它需要session存储用户信息。

user属性的添加,被延迟到了get_user()函数里。SimpleLazyObject是一种延迟的技术。


在来看SessionAuthenticationMiddleware的定义:

它负责session验证

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class  SessionAuthenticationMiddleware( object ):
     """
     Middleware for invalidating a user's sessions that don't correspond to the
     user's current session authentication hash (generated based on the user's
     password for AbstractUser).
     """
     def  process_request( self , request):
         user  =  request.user
         if  user  and  hasattr (user,  'get_session_auth_hash' ):
             session_hash  =  request.session.get(auth.HASH_SESSION_KEY)
             session_hash_verified  =  session_hash  and  constant_time_compare(
                 session_hash,
                 user.get_session_auth_hash()
             )
             if  not  session_hash_verified:
                 auth.logout(request)

通过比较user的get_session_auth_hash方法,和session里面的auth.HASH_SESSION_KEY属性,判断用户的session是否正确。


至于request里面的user对象,由有什么属性,需要看看get_user()函数的定义。

?
1
2
3
4
def  get_user(request):
     if  not  hasattr (request,  '_cached_user' ):
         request._cached_user  =  auth.get_user(request)
     return  request._cached_user

显然get_user方法在request增加了_cached_user属性,用来作为缓存。

因为用户认证需要查询数据库,得到用户的信息,所以减少开销是有必要的。

注意,这种缓存只针对同一个request而言的,即在一个view中多次访问request.user属性。

每次http请求都是新的request。


再接着看auth.get_user()方法的定义,深入了解request.user这个对象:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def  get_user(request):
     """
     Returns the user model instance associated with the given request session.
     If no user is retrieved an instance of `AnonymousUser` is returned.
     """
     from  .models  import  AnonymousUser
     user  =  None
     try :
         user_id  =  request.session[SESSION_KEY]
         backend_path  =  request.session[BACKEND_SESSION_KEY]
     except  KeyError:
         pass
     else :
         if  backend_path  in  settings.AUTHENTICATION_BACKENDS:
             backend  =  load_backend(backend_path)
             user  =  backend.get_user(user_id)
     return  user  or  AnonymousUser()


首先它会假设客户端和服务器已经建立session机制了,这个session中的SESSION_KEY属性,就是user的id号。

这个session的BACKEND_SESSION_KEY属性,就是指定使用哪种后台技术获取用户信息。最后使用backend.get_user()获取到user。如果不满足,就返回AnonymousUser对象。


从这个获取user的过程,首先有个前提,就是客户端与服务端得先建立session机制。那么这个session机制是怎么建立的呢?

这个session建立的过程在auth.login函数里:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def  login(request, user):
     """
     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  is  None :
         user  =  request.user
     if  hasattr (user,  'get_session_auth_hash' ):
         session_auth_hash  =  user.get_session_auth_hash()
 
     if  SESSION_KEY  in  request.session:
         if  request.session[SESSION_KEY] ! =  user.pk  or  (
                 session_auth_hash  and
                 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()
     request.session[SESSION_KEY]  =  user.pk
     request.session[BACKEND_SESSION_KEY]  =  user.backend
     request.session[HASH_SESSION_KEY]  =  session_auth_hash
     if  hasattr (request,  'user' ):
         request.user  =  user
     rotate_token(request)


首先它会判断是否存在与用户认证相关的session,如果有就清空数据,如果没有就新建。

然后再写如session的值:SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY。


然后讲一下登录时,使用auth通常的做法:

?
1
2
3
4
5
6
7
8
9
from  django.contrib.auth  import  authenticate, login 
def  login_view(request):
     username  =  request.POST[ 'username' ]
     password  =  request.POST[ 'password' ]
     user  =  authenticate(username = username, password = password)
     if  user  is  not  None :
         login(request, user)
         # 转到成功页面
     else :         # 返回错误信息

一般提交通过POST方式提交,然后调用authenticate方法验证,成功后使用login创建session。


继续看看authenticate的定义:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def  authenticate( * * credentials):
     """
     If the given credentials are valid, return a User object.
     """
     for  backend  in  get_backends():
         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  =  "%s.%s"  %  (backend.__module__, backend.__class__.__name__)
         return  user
 
     # The credentials supplied are invalid to all backends, fire signal
     user_login_failed.send(sender = __name__,
             credentials = _clean_credentials(credentials))

它会去轮询backends,通过调用backend的authenticate方法认证。

注意它在后面更新了user的backend属性,表明此用户是使用哪种backend认证方式。它的值会在login函数里,被存放在session的BACKEND_SESSION_KEY属性里。

通过backend的authenticate方法返回的user,是没有这个属性的。


最后说下登录以后auth的用法。上面展示了登录时auth的用法,在登录以后,就会建立session机制。所以直接获取request的user属性,就可以判断用户的信息和状态。

?
1
2
3
4
5
def  my_view(request):
     if  request.user.is_authenticated():
         # 认证的用户
     else :    
         # 匿名用户
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值