详解django.contirb.auth-认证


Posted in Python onJuly 16, 2018

首先看middleware的定义:

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

AuthenticationMiddleware负责向request添加user属性

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验证

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()函数的定义。

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这个对象:

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函数里:

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通常的做法:

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的定义:

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属性,就可以判断用户的信息和状态。

def my_view(request):
  if request.user.is_authenticated():
    # 认证的用户
  else:  
    # 匿名用户

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python函数的5种参数详解
Feb 24 Python
基于Django模板中的数字自增(详解)
Sep 05 Python
Python实现定时备份mysql数据库并把备份数据库邮件发送
Mar 08 Python
python基础教程项目四之新闻聚合
Apr 02 Python
如何利用python制作时间戳转换工具详解
Sep 12 Python
实例详解python函数的对象、函数嵌套、名称空间和作用域
May 31 Python
Python 监测文件是否更新的方法
Jun 10 Python
树莓派实现移动拍照
Jun 22 Python
解决pyshp UnicodeDecodeError的问题
Dec 06 Python
解决TensorFlow GPU版出现OOM错误的问题
Feb 03 Python
django实现模型字段动态choice的操作
Apr 01 Python
利用scikitlearn画ROC曲线实例
Jul 02 Python
Python爬虫使用脚本登录Github并查看信息
Jul 16 #Python
django认证系统实现自定义权限管理的方法
Jul 16 #Python
Sanic框架路由用法实例分析
Jul 16 #Python
Sanic框架安装与简单入门示例
Jul 16 #Python
python 除法保留两位小数点的方法
Jul 16 #Python
Python自定义装饰器原理与用法实例分析
Jul 16 #Python
python 正确保留多位小数的实例
Jul 16 #Python
You might like
收藏的PHP常用函数 推荐收藏保存
2010/02/21 PHP
提高PHP编程效率的53个要点(经验小结)
2010/09/04 PHP
从零开始学YII2框架(三)扩展插件yii2-gird
2014/08/20 PHP
变量在 PHP7 内部的实现(二)
2015/12/21 PHP
PHP基于IMAP收取邮件的方法示例
2017/08/07 PHP
PHP实现的XXTEA加密解密算法示例
2018/08/28 PHP
PHP实现图片防盗链破解操作示例【解决图片防盗链问题/反向代理】
2020/05/29 PHP
JavaScript中getUTCMinutes()方法的使用详解
2015/06/10 Javascript
JavaScript基于activexobject连接远程数据库SQL Server 2014的方法
2017/07/12 Javascript
jQuery模仿ToDoList实现简单的待办事项列表
2019/12/30 jQuery
H5+css3+js搭建带验证码的登录页面
2020/10/11 Javascript
vue实现拖拽进度条
2021/03/01 Vue.js
Python中不同进制互相转换(二进制、八进制、十进制和十六进制)
2015/04/05 Python
python实现DES加密解密方法实例详解
2015/06/30 Python
深入讲解Python中的迭代器和生成器
2015/10/26 Python
讲解Python的Scrapy爬虫框架使用代理进行采集的方法
2016/02/18 Python
浅谈python中set使用
2016/06/30 Python
python中Apriori算法实现讲解
2017/12/10 Python
PyCharm+PySpark远程调试的环境配置的方法
2018/11/29 Python
Python3爬虫学习入门教程
2018/12/11 Python
详解重置Django migration的常见方式
2019/02/15 Python
Django组件content-type使用方法详解
2019/07/19 Python
Python脚本打包成可执行文件过程解析
2020/10/20 Python
python 如何读、写、解析CSV文件
2021/03/03 Python
纯CSS3实现滚动的齿轮动画效果
2014/06/05 HTML / CSS
前端隐藏出边界内容的实现方法
2016/04/14 HTML / CSS
马来西亚与新加坡长途巴士售票网站:BusOnlineTicket.com
2018/11/05 全球购物
英国DVD和蓝光碟片购买网站:Zoom.co.uk(电影和电视)
2019/09/23 全球购物
请问如下代码执行后a和b的值分别是什么
2016/05/05 面试题
人事文员岗位职责
2014/02/16 职场文书
党的群众路线教育实践活动对照检查材料思想汇报
2014/09/19 职场文书
小学校园广播稿(3篇)
2014/09/19 职场文书
毕业实习证明范本
2015/06/16 职场文书
健身房被搭讪?用python写了个小米计时器助人为乐
2021/06/08 Python
springboot中rabbitmq实现消息可靠性机制详解
2021/09/25 Java/Android
微信小程序结合ThinkPHP5授权登陆后获取手机号
2021/11/23 PHP