首先看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
):
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):
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):
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):
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):
for
backend
in
get_backends():
try
:
inspect.getcallargs(backend.authenticate,
*
*
credentials)
except
TypeError:
continue
try
:
user
=
backend.authenticate(
*
*
credentials)
except
PermissionDenied:
return
None
if
user
is
None
:
continue
user.backend
=
"%s.%s"
%
(backend.__module__, backend.__class__.__name__)
return
user
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
: