客户在访问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. python matplotlib

    背景: 1)数据可视化 目前还处于python入门阶段,主要想通过numpy.matplotlib进行数据可视化. 安装: 操作系统:windows7 1)python2.7 安装numpy.matp ...

  2. [SRS流媒体]RTMP/HLS 直播服务器simple-rtmp-server安装

    一个采用MIT协议授权的国产的简单的RTMP/HLS 直播服务器,其核心的价值理念在于简单高效. 使用方法: tep 1: build srs tar xf simple-rtmp-server-*. ...

  3. android实现qq邮箱多个图标效果

    前几天,蛋疼的技术主管非要实现类似装一个qq邮箱,然后能够使用qq邮箱日历的那么一个东西.相当于一个应用生成两个图标,可是不同的是点击不同的图标能够进入不同的应用,例如以下图的效果. 这效果百度了一天 ...

  4. apache 80端口部属多站点配置

    1.在httpd.conf文件里启用虚拟主机功能,即去掉下面配置项前面的# #LoadModule vhost_alias_module modules/mod_vhost_alias.so 2..在 ...

  5. [转]Sublime Text3注册码(可用)

    补充:2016.05 最近经过测试,3个注册码在新版3103的sublime上已经不可用了. 现补充两枚新版的license key: —– BEGIN LICENSE —– Michael Barn ...

  6. 搞不清FastCgi与PHP-fpm之间是个什么样的关系(转载)

    刚开始对这个问题我也挺纠结的,看了<HTTP权威指南>后,感觉清晰了不少. 首先,CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的,方便CGI程序的编写者. ...

  7. adding validation annotators to model classes 在linq to EntityFrame的Model中添加前台验证validation annotators

    The same solution can be applied for LINQ to SQL. The snippet the article shows for using the Metada ...

  8. ch2 创建和销毁对象

    ch2 创建和销毁对象              

  9. Arduino 学习

    Arduino 背景可以参考官方网站www.arduino.cc 先看一个最简单的示例程序: 打开 Arduino IDE , 选择菜单:文件 -> 示例 -> 01.Basics -&g ...

  10. JS - Cookie: getCookie, setCookie

    JS function for Cookie 如果cookie未设置,判断时与空字符串‘’比较: function setCookie(cname, cvalue, exdays) { var d = ...