客户在访问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。

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

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))

  1.  

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. FTP客户端上传下载Demo实现

    1.第一次感觉MS也有这么难用的MFC类: 2.CFtpFileFind类只能实例化一个,多个实例同时查找会出错(因此下载时不能递归),采用队列存储目录再依次下载: 3.本程序支持文件夹嵌套上传下载: ...

  2. nodejs express 框架解密1-总体结构

    本文是基于express3.4.6的. 1.express 代码结构为: bin/express 是在命令行下的生成express 框架目录文件用的 lib/express 是框架的入口文件 lib/ ...

  3. Centos单网卡配置多个IP的方法

    网上好多介绍一个网卡多个IP的方法,都有些问题,下面是实践可行的方法 DEVICE="eth0" TYPE="Ethernet" BOOTPROTO=none ...

  4. Asp.net Core WebApi 返回JSON自动驼峰格式化问题

    从今天开始,正式进入Asp.net Core的开发,估计最近一段时间会经常写博客了,记录学些Asp.net Core中遇到的各种坑. 第一个问题:通过core编写的webapi,默认返回的json会自 ...

  5. 解决“com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536”问题(l转)

    同时在工程中引入了多个第三方jar包,导致调用的方法数超过了android设定的65536个(DEX 64K problem),进而导致dex无法生成,也就无法生成APK文件. 解决办法如下: 1.谷 ...

  6. C#3.0新特性之扩展方法介绍

    C#3.0扩展方法是给现有类型添加一个方法.现在类型即可是基本数据类型(如int,String等),也可以是自己定义的类.以下是引用片段: //Demo--1 //扩展基本类型 namespace T ...

  7. ORA-12571: TNS:packet writer failure

    ORA-12571:TNS:包复写器失效 2007-05-28 22:04 ORA-12571:TNS:包复写器失效 近日一直在做Oracle数据库的搭建(入门),在搭建过程中遇到“ORA-12571 ...

  8. SQL语句调优 - 索引上的数据检索方法

    如果一张表上没有聚集索引,数据将会随机的顺序存放在表里.以dbo.SalesOrderDetail_TEST为例子.它的上面没有聚集索引,只有一个在SalesOrderID上的非聚集索引.所以表格的每 ...

  9. SymmetricDS 数据库双向同步开源软件入门

    一句话概括该软件:SymmetricDS是一个文件和数据库同步软件,开源的,支持多主复制,同步时过滤和在异构的网络环境中进行数据转换传输.它支持单向和双向上的多个订阅者,异步的数据复制. 以下是从CS ...

  10. OpenSSL Command-Line HOWTO

    OpenSSL Command-Line HOWTO The openssl application that ships with the OpenSSL libraries can perform ...