zoukankan      html  css  js  c++  java
  • 三、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实体。

  • 相关阅读:
    VS2010中使用JSONCPP方法
    VC获取外网IP
    JSON样例
    JSON详解
    vc获取本地IP
    Java中创建对称密钥的代码
    密和解密程序的一些概念
    在ireport报错 报 jdk5找不到的解决办法
    Java中创建对称密钥的步骤
    比较好用的一个jaspereport模板 生成html页面模板
  • 原文地址:https://www.cnblogs.com/fbli/p/5925075.html
Copyright © 2011-2022 走看看