客户在访问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. #pragma data_seg 共享数据区(转)

    原文地址:http://www.cnblogs.com/CBDoctor/archive/2013/01/26/2878201.html 1)#pragma data_seg()一般用于DLL中.也就 ...

  2. linux crontab 文件位置和日志位置

    一.文件位置 位置一般在/var/spool/cron/下,如果你是root用户,那下面有个root文件,建议日常备份,避免误删除导致crontab 文件丢失: 二.日志文件位置 默认情况下,cron ...

  3. spring-boot 加载本地静态资源文件路径配置

    1.spring boot默认加载文件的路径是 /META-INF/resources/ /resources/ /static/ /public/ 这些目录下面, 当然我们也可以从spring bo ...

  4. 每日英语:A Different Color: China's Chameleonic Politics

    China has tried your form of government and found it wanting. That was the message delivered by Chin ...

  5. 关于VS2010出现“此方法显式使用的 CAS 策略已被 .NET Framework 弃用... ...请使用 NetFx40_LegacySecurityPolicy 配置开关”解决办法

    有时候VS会出现“此方法显式使用的 CAS 策略已被 .NET Framework 弃用.若要出于兼容性原因而启用 CAS 策略,请使用 NetFx40_LegacySecurityPolicy 配置 ...

  6. 【LeetCode】258. Add Digits (2 solutions)

    Add Digits Given a non-negative integer num, repeatedly add all its digits until the result has only ...

  7. [转]Android 超高仿微信图片选择器 图片该这么加载

    快速加载本地图片缩略图的方法: 原文地址:Android 超高仿微信图片选择器 图片该这么加载 其示例代码下载: 仿微信图片选择器 ImageLoader

  8. android Animation 动画绘制逻辑

    参考:http://www.jianshu.com/p/3683a69c38ea 1.View.draw(Canvas) 其中步骤为:/* * Draw traversal performs seve ...

  9. ubuntu下搭建lamp

    一.使用apt-get方式为Ubuntu安装PHP+MYSQL+Apache   分别执行如下命令:   (1)安装MYSQL   sudo apt-get install mysql-server ...

  10. 再谈LRU双链表内存管理

    N年前我写了个双链表也发了博客,还添了代码.但是那个代码不但复杂,而且还有有问题的,一直懒得整理,放在空间误导别人.最近在写服务端,今天抽点空补一篇. 关于LRU网上随便搜,有过后端经验的人应该很多都 ...