客户在访问Django的某些敏感资料时,被要求需要先登录,客户通过/admin/login进行登录,客户登录成功后,Django给客户分配一个sessionid,后续的访问过程,客户端只需在http头部的cookie中携带该sessionid即可完成认证,无需每次都携带用户名和密码。

因此这里需要完成下面这些工作:

1、首次登陆(/admin/login),如何获取用户名和密码,并进行认证和登录?

2、用户认证,登录后,如何将用户名(userid)生成sessionid,并返回个客户?

3、用户再次登录时,在cookie中携带返回的sessionid,Django如何将sessionid转换为对应的userid?

1.1       类图

先来看看涉及到的类图及其关系图。首当其冲的是HTTPRequest(request)这个类,前面有介绍过,这里重点关注该模块涉及到的COOKIES,session和user。COOKIES主要以字典形式存储了从HTTP头部解析到的cookie信息,包括csrftoken,sessionid等信息。

Session是对应SessionStore类,session提供多个引擎可供使用,引擎实质是不同的存储机制,Django提供了多达五个session引擎,分别为:file,db,cached_db,cache,base,这些引擎都存储在django/contrib/sessions/backends中,通过global_setting.py(或者setting.py)进行设置和选择。以db引擎为例, 其主要涉及到的成员和函数有:

SessionStore存储形式主要以哈希表的形式存在,即:键:键值,常见的键及其键值如下:

KEY/宏

KEY

MEANING

SESSION_KEY

_auth_user_id

Username

BACKEND_SESSION_KEY

_auth_user_backend

django.contrib.auth.backends.ModelBackend,提供认证,鉴权机制

HASH_SESSION_KEY

_auth_user_hash

对应auth_session表中Session_data,基于用户密码哈希运算得到

request.session.session_key 存储了http解析到的以及更新后的sessionid。

django_session数据表中存在三个字段,分别为session_key,session_data,expire_data。其中session_key为对应的sessionid(request.session.session_key),而session_data是[_auth_user_id, _auth_user_backend, _auth_user_id]组成的字典经过base64加密后的结果。

User模型主要基于models.AbstractUser类。

1.2       首次认证以及登录过程

客户通过在浏览器中输入POST http://server_ip:server_port/admin/login, 并且在httpbody里面携带用户名和密码后发起认证过程。Admin模块通过url匹配进入:django. Contrib. admin. sites.login。

AdminSite.login(self, request, extra_context=None)à
   return login(request, **defaults)à
      form = authentication_form(request, data=request.POST)
      if form.is_valid():     /*判断用户输入是否有效*/
          auth_login(request, form.get_user())       /*登录*/
          return HttpResponseRedirect(request, redirect_to) /*登录成功跳转页面*/
 
      return TemplateResponse(request, template_name, context)/*其余情况呈现login界面*/
 
login(request, user, backend=None)à
  session_auth_hash = user.get_session_auth_hash()  /*生成hash */
/*To avoid reusing another user's session, create a new, empty
# session if the existing session corresponds to a different
# authenticated user.*/
  if SESSION_KEY in request.session:
     _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)):
  else: request.session.cycle_key() /*Creates a new session key, while retaining the current session data.*/
        
/*存储当前session值,主要是 userid,backend, session_auth_hash*/
  request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
request.session[BACKEND_SESSION_KEY] = backend
request.session[HASH_SESSION_KEY] = session_auth_hash
  
rotate_token(request)  /*每次重新登陆后,更新csrftoken*/
 

request.session.cycle_key()这一步骤生成新的sessionid, 保存在request.session.session_key中,剩下的工作就是通过SessionMiddleware的process_response()调用将生成的sessionid以及其对应的属性(如expire等)传递给客户,客户下次请求直接在cookie携带该sessionid即可,而无需每次都携带用户名和密码等信息。

1.3       再次认证过程

客户在前面登录的基础上,再次访问django服务,django在解析到sessionid后,将sessionid转换为对应的userid,即完成认证过程。这里依赖两个中间件来实现,分别为:SessionMiddleware和AuthenticationMiddleware。

SessionMiddleware最先对到来的http请求进行处理,将从头部解析到的到的sessionid复制到request.session.session_key,并初始化一个SessionStore实体后赋值给request.session。

def process_request(self, request):

session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)

request.session = self.SessionStore(session_key)

AuthenticationMiddleware接着对到来的http请求进行处理,获取到用户信息,具体通过:

def process_request(self, request):

request.user = SimpleLazyObject(get_user(request))

 

def get_user(request):

if not hasattr(request, '_cached_user'):  /*这里利用了缓存机制,加快处理*/

request._cached_user = auth.get_user(request)

return request._cached_user

下面来看看django.contrb.auth中get_user()如何实现从sessionid到userid的转换的。

def get_user(request):

user_id = _get_user_session_key(request)

backend_path = request.session[BACKEND_SESSION_KEY]

user = backend.get_user(user_id)

# Verify the session做一下简单的校验

if hasattr(user, 'get_session_auth_hash'):

session_hash = request.session.get(HASH_SESSION_KEY)

session_hash_verified = session_hash and constant_time_compare(

session_hash,

user.get_session_auth_hash()

)

if not session_hash_verified:

request.session.flush()

user = None

return user or AnonymousUser()

前面有介绍过在django_session数据表中的session_data字段,存储了(_auth_user_id, _auth_user_backend, _auth_user_id)这三个信息,因此通过session_key(/sessionid)可以方便的查询到_auth_user_id,即对应的userid。通过userid获取user实体就更简单了,因为userid是auth_user表的主键,通过主键值可以快捷的获取user实体。

三、Authentication & sessionid的更多相关文章

  1. 分布式Session共享(一):tomcat+redis实现session共享

    一.前言 本文主要测试redis实现session共享的实现方式,不讨论如何让nginx参与实现负载均衡等. 二.环境配置 本测试在Window下进行 name version port Tomcat ...

  2. 分布式Session共享(二):tomcat+memcached实现session共享

    一.前言 本文主要测试memcached实现session共享的实现方式,不讨论如何让nginx参与实现负载均衡等. 二.环境配置 本测试在Window下进行 name version port To ...

  3. [转]ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

    本文转自:http://www.cnblogs.com/parry/p/ASPNET_MVC_Web_API_digest_authentication.html 在前一篇文章中,主要讨论了使用HTT ...

  4. ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

    在前一篇文章中,主要讨论了使用HTTP基本认证的方法,因为HTTP基本认证的方式决定了它在安全性方面存在很大的问题,所以接下来看看另一种验证的方式:digest authentication,即摘要认 ...

  5. CAS 之 Https And Database Authentication(三)

    CAS 之 Https And Database Authentication(三) 标签(空格分隔): CAS sso-examples-guides源码 Intro(介绍) 由上节可知Apereo ...

  6. Apache shiro集群实现 (三)shiro身份认证(Shiro Authentication)

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  7. HttpClient 三种 Http Basic Authentication 认证方式,你了解了吗?

    Http Basic 简介 HTTP 提供一个用于权限控制和认证的通用框架.最常用的 HTTP 认证方案是 HTTP Basic authentication.Http Basic 认证是一种用来允许 ...

  8. Web APi之认证(Authentication)两种实现方式后续【三】(十五)

    前言 之前一直在找工作中,过程也是令人着实的心塞,最后还是稳定了下来,博客也停止更新快一个月了,学如逆水行舟,不进则退,之前学的东西没怎么用,也忘记了一点,不过至少由于是切身研究,本质以及原理上的脉络 ...

  9. .NetCore源码阅读笔记系列之Security (三) Authentication & AddOpenIdConnect

    通过第二篇文章我们已经知道了授权的内部实现通过自定义的授权Handler来的,同样的道理 OpenIdConnect 同样是通过 OpenIdConnectHandler来请求授权的 那么它内部又是怎 ...

随机推荐

  1. Android中GPS类及方法简介

    GPS是Global Positioning System(全球定位系统)的简称,它的作用就是为全球的物体提供定位功能.GPS定位是一门高新技术,但对于Android程序员来说,开发GPS功能的应用程 ...

  2. Scala access modifiers and qualifiers in detail

    来自:http://www.jesperdj.com/2016/01/08/scala-access-modifiers-and-qualifiers-in-detail/ Just like Jav ...

  3. Java 7 jps - JVM Process Status Tool

    本文内容 语法 参数 描述 选项 主机标识符 输出格式 示例 参考资料 先发出来,然后慢慢翻译~ 语法 jps [ options ] [ hostid ] 参数 options 命令行参数. hos ...

  4. Android的Activity屏幕切换动画(一)-左右滑动切换

    (国内知名Android开发论坛eoe开发者社区推荐:http://www.eoeandroid.com/) Android的Activity屏幕切换动画(一)-左右滑动切换 在Android开发过程 ...

  5. oracle导入导出小记

    问题:11.2.0.3.0 导入  11.2.0.2.0 都是oracle 11g ,从0.3.0到0.2.0 报错,以为是版本问题,结果不是 采用impdp 导入exp导出的文件会报错 所以改为im ...

  6. 移动APP接口遇到的一些小问题

    一:IIS设置站点后无法访问apk文件 首先要给IIS服务器根目录添加MIME类型影射文件扩展名:apkMIME类型 :application/vnd.android.package-archive ...

  7. js计时器 + asp 计时器

    JS: <script type="text/javascript"> ; function starts() { ) { alert('已经开启了实时监控!') re ...

  8. 一篇不错的讲解Java异常的文章(转载)原作者已没法考证

    六种异常处理的陋习 你觉得自己是一个Java专家吗?是否肯定自己已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStreamWriter ...

  9. 解决cxf+spring发布的webservice,types,portType和message以import方式导入

    用cxf+spring发布了webservice,发现生成的wsdl的types,message和portType都以import的方式导入的.. 原因:命名空间问题 我想要生成的wsdl在同个文件中 ...

  10. Spark源码系列(一)spark-submit提交作业过程

    前言 折腾了很久,终于开始学习Spark的源码了,第一篇我打算讲一下Spark作业的提交过程. 这个是Spark的App运行图,它通过一个Driver来和集群通信,集群负责作业的分配.今天我要讲的是如 ...