三、Authentication & sessionid
客户在访问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的更多相关文章
- 分布式Session共享(一):tomcat+redis实现session共享
一.前言 本文主要测试redis实现session共享的实现方式,不讨论如何让nginx参与实现负载均衡等. 二.环境配置 本测试在Window下进行 name version port Tomcat ...
- 分布式Session共享(二):tomcat+memcached实现session共享
一.前言 本文主要测试memcached实现session共享的实现方式,不讨论如何让nginx参与实现负载均衡等. 二.环境配置 本测试在Window下进行 name version port To ...
- [转]ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)
本文转自:http://www.cnblogs.com/parry/p/ASPNET_MVC_Web_API_digest_authentication.html 在前一篇文章中,主要讨论了使用HTT ...
- ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)
在前一篇文章中,主要讨论了使用HTTP基本认证的方法,因为HTTP基本认证的方式决定了它在安全性方面存在很大的问题,所以接下来看看另一种验证的方式:digest authentication,即摘要认 ...
- CAS 之 Https And Database Authentication(三)
CAS 之 Https And Database Authentication(三) 标签(空格分隔): CAS sso-examples-guides源码 Intro(介绍) 由上节可知Apereo ...
- Apache shiro集群实现 (三)shiro身份认证(Shiro Authentication)
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- HttpClient 三种 Http Basic Authentication 认证方式,你了解了吗?
Http Basic 简介 HTTP 提供一个用于权限控制和认证的通用框架.最常用的 HTTP 认证方案是 HTTP Basic authentication.Http Basic 认证是一种用来允许 ...
- Web APi之认证(Authentication)两种实现方式后续【三】(十五)
前言 之前一直在找工作中,过程也是令人着实的心塞,最后还是稳定了下来,博客也停止更新快一个月了,学如逆水行舟,不进则退,之前学的东西没怎么用,也忘记了一点,不过至少由于是切身研究,本质以及原理上的脉络 ...
- .NetCore源码阅读笔记系列之Security (三) Authentication & AddOpenIdConnect
通过第二篇文章我们已经知道了授权的内部实现通过自定义的授权Handler来的,同样的道理 OpenIdConnect 同样是通过 OpenIdConnectHandler来请求授权的 那么它内部又是怎 ...
随机推荐
- php中抓取网页内容的代码
方法一: 使用file_get_contents方法实现 $url = "http://news.sina.com.cn/c/nd/2016-10-23/doc-ifxwztru695114 ...
- IOS APP 国际化 程序内切换语言实现 不重新启动系统(支持项目中stroyboard 、xib 混用。完美解决方案)
上篇 IOS APP 国际化(实现不跟随系统语言,不用重启应用,代码切换stroyboard ,xib ,图片,其他资源 介绍了纯代码刷新 实现程序内切换语言. 但效率底下,也存在一些问题.暂放弃. ...
- 国内市场上 Android 手机屏幕分辨率的比例情况如何?
http://www.zhihu.com/question/19587205 根据友盟发布的<友盟国内Android数据报告>,前六名分别是: 800×480,32.4% 480×320, ...
- 39. Volume Rendering Techniques
Milan Ikits University of Utah Joe Kniss University of Utah Aaron Lefohn University of California, D ...
- 11G RAC 中 OCR 及Voting Disk 相关操作
一.启动oracle clusterware先决条件:Oracle High Availability Services daemon(OHASD)运行在所有集群节点上1.启动整个Oracle Clu ...
- SharedPreference注册OnSharedPreferenceChangeListener一直无法回调问题
注册代码如下: SharedPreferences sp = getSharedPreferences("AndroidDemo", Context.MODE_PRIVATE); ...
- Carthage 安装和使用
和Cocoapods相比各有利弊吧,具体对比参见: Carthage 初探:四大优势与四大劣势 第一步:如果没有安装Homebrew先安装 打开命令终端,直接输入以下命令回车 /usr/bin/rub ...
- 几种常用远程通信技术(RPC,Webservice,RMI,JMS)的区别
原文链接:http://blog.csdn.net/shan9liang/article/details/8995023 RPC(Remote Procedure Call Protocol) RPC ...
- css3彩色进度条
<html> <head> <title>progress</title> <script type=" ...
- js 事件捕获与事件冒泡例子
http://codepen.io/huashiyiqike/pen/qZVdag addEventListener 默认是冒泡阶段执行,也就是父亲与子都监听时,点击子,子先处理,父亲再处理,这时加s ...