Openstack --- Horizon之login

前言

笔者已经搭建了一个openstack的开发环境,能够调试运行Horizon工程,在本地的目录结构如下:
/horizon
├── babel-django.cfg
├── babel-djangojs.cfg
├── base.py
├── browsers
├── conf
├── context_processors.py
├── contrib
├── CONTRIBUTING.rst
├── decorators.py
├── doc
├── exceptions.py
├── forms
├── hacking
├── HACKING.rst
├── horizon
├── __init__.py
├── karma.conf.js
├── LICENSE
├── loaders.py
├── locale
├── Makefile
├── management
├── manage.py
├── MANIFEST.in
├── messages.py
├── middleware.py
├── notifications.py
├── openstack_dashboard
├── package.json
├── README.rst
├── releasenotes
├── requirements.txt
├── run_tests.sh
├── setup.cfg
├── setup.py
├── site_urls.py
├── static
├── tables
├── tabs
├── templates
├── templatetags
├── test
├── test-requirements.txt
├── test-shim.js
├── themes.py
├── tools
├── tox.ini
├── utils
├── venv
├── version.py
├── views.py
└── workflows
其中venv路径下包含了本地运行horizon工程的python环境,运行时指定工程的解释器为venv中python。并且horizon工程能够运行需要keystone服务,在此已经配置了正常运行的keystone环境。代码分析是基于M版本的openstack-horizon工程。

urls

一般读工程代码我习惯性从url入手,这样方便找到对应连接的响应函数,horizon工程中的horizon目录下是内容是horizon提供的库,其dashboard的核心在openstack_dashboard目录下,其中的url文件就是整个dashboard的主url文件,中urlpatterns内容如下:

import horizon

urlpatterns = patterns(
    '',
    url(r'^$', 'openstack_dashboard.views.splash', name='splash'),
    url(r'^api/', include('openstack_dashboard.api.rest.urls')),
    url(r'', include(horizon.urls)),
)

for u in getattr(settings, 'AUTHENTICATION_URLS', ['openstack_auth.urls']):
    urlpatterns += patterns(
        '',
        url(r'^auth/', include(u))
    )

# Development static app and project media serving using the staticfiles app.
urlpatterns += staticfiles_urlpatterns()

# Convenience function for serving user-uploaded media during
# development. Only active if DEBUG==True and the URL prefix is a local
# path. Production media should NOT be served by Django.
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

if settings.DEBUG:
    urlpatterns += patterns(
        '',
        url(r'^500/$', 'django.views.defaults.server_error')
    )

整个文件中的内容,在一些openstack-horizon源码分析的网站有介绍,此处不在赘述。但是打开文件并未发现login的入口,此处提供一种通过django工程url链接寻找代码入口的方法:
用工程manager.py文件打开shell终端(注意此处一定要是运行工程环境的python解释器路径):

root@ubuntu:~/horizon$ ./venv/bin/python manage.py shell
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.core.urlresolvers import reverse, resolve
>>> func, args, kwargs = resolve(reverse('login'))
Could not process panel theme_preview: Dashboard with slug "developer" is not registered.
>>> func.__module__
'openstack_auth.views'
>>>

该文件是 ./venv/lib/python2.7/site-packages/openstack_auth下的views.py文件,所以可以明白openstack_dashboard下url.py中:

for u in getattr(settings, 'AUTHENTICATION_URLS', ['openstack_auth.urls']):
    urlpatterns += patterns(
        '',
        url(r'^auth/', include(u))
    )

就是将openstack_auth下的urls.py文件中的url包含进来,代码如下:

from django.conf.urls import url
from openstack_auth import utils
from openstack_auth import views
utils.patch_middleware_get_user()
urlpatterns = [
    url(r"^login/$", views.login, name='login'),
    url(r"^logout/$", views.logout, name='logout'),
    url(r'^switch/(?P<tenant_id>[^/]+)/$', views.switch,
        name='switch_tenants'),
    url(r'^switch_services_region/(?P<region_name>[^/]+)/$',
        views.switch_region,
        name='switch_services_region')
]
if utils.is_websso_enabled():
    urlpatterns.append(url(r"^websso/$", views.websso, name='websso'))

Login代码分析

上urls.py中login链接的响应函数代码如下:

@sensitive_post_parameters()
@csrf_protect
@never_cache
def login(request, template_name=None, extra_context=None, **kwargs):
    """Logs a user in using the :class:`~openstack_auth.forms.Login` form."""

    # If the user enabled websso and selects default protocol
    # from the dropdown, We need to redirect user to the websso url

    if request.method == 'POST':
        auth_type = request.POST.get('auth_type', 'credentials')
        if utils.is_websso_enabled() and auth_type != 'credentials':
            auth_url = request.POST.get('region')
            url = utils.get_websso_url(request, auth_url, auth_type)
            return shortcuts.redirect(url)

    if not request.is_ajax():
        # If the user is already authenticated, redirect them to the
        # dashboard straight away, unless the 'next' parameter is set as it
        # usually indicates requesting access to a page that requires different
        # permissions.
        if (request.user.is_authenticated() and
                auth.REDIRECT_FIELD_NAME not in request.GET and
                auth.REDIRECT_FIELD_NAME not in request.POST):
            return shortcuts.redirect(settings.LOGIN_REDIRECT_URL)

    # Get our initial region for the form.
    initial = {}
    current_region = request.session.get('region_endpoint', None)
    requested_region = request.GET.get('region', None)
    regions = dict(getattr(settings, "AVAILABLE_REGIONS", []))
    if requested_region in regions and requested_region != current_region:
        initial.update({'region': requested_region})

    if request.method == "POST":
        form = functional.curry(forms.Login)
    else:
        form = functional.curry(forms.Login, initial=initial)

    if extra_context is None:
        extra_context = {'redirect_field_name': auth.REDIRECT_FIELD_NAME}

    if not template_name:
        if request.is_ajax():
            template_name = 'auth/_login.html'
            extra_context['hide'] = True
        else:
            template_name = 'auth/login.html'

    res = django_auth_views.login(request,
                                  template_name=template_name,
                                  authentication_form=form,
                                  extra_context=extra_context,
                                  **kwargs)
    # Save the region in the cookie, this is used as the default
    # selected region next time the Login form loads.
    if request.method == "POST":
        utils.set_response_cookie(res, 'login_region',
                                  request.POST.get('region', ''))
        utils.set_response_cookie(res, 'login_domain',
                                  request.POST.get('domain', ''))

    # Set the session data here because django's session key rotation
    # will erase it if we set it earlier.
    if request.user.is_authenticated():
        auth_user.set_session_from_user(request, request.user)
        regions = dict(forms.Login.get_region_choices())
        region = request.user.endpoint
        login_region = request.POST.get('region')
        region_name = regions.get(login_region)
        request.session['region_endpoint'] = region
        request.session['region_name'] = region_name
    return res

Openstack dashboard中链接都是做过login_required限定,即访问必须用户登陆才能进行。当未登陆访问时首先跳转到‘/auth/login/?next=/’, 当然参数next可能是其他值(例如你在/project 页面会话过期,需要重新登陆,此时next就是/project,成功登陆后下一跳地址)。

三个装饰器:

(1) @sensitive_post_parameters()
Debug时候用来跟踪某些敏感变量,时刻能够观察敏感变量中的值。
(2) @csrf_protect
Django csrf保护 https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/
(3) @never_cache
禁止使用缓存视图 http://djangobook.py3k.cn/2.0/chapter15/

Login 页面的GET请求

此时request.method 为GET,request.user.is_authenticated()为False,可见程序运行主线为:
获取initial, regions等值 –> 指定form表单并用initial初始化 –> 指定下一跳参数名称为next –> 指定模板为auth/login.html –> 利用django_auth_views.login()函数返回请求结果。

login部分页面位于horizon包中的templates/auth下
auth
├── _description.html
├── _login_form.html
├── _login.html
├── login.html
├── _login_modal.html
├── _login_page.html
└── _splash.html

login.html —include—> _login.html —include—> _login_page.html —extend—> _login_form.html
利用django form表单反向生成登陆页面,完成GET请求显示登陆界面。

Login页面POST请求

登陆界面填写用户名密码后,点击提交即发送POST请求到auth/login/链接。此时request.method 为POST,用户名密码如果匹配则request.user.is_authenticated()为True,否则为False。结之前提供源码,此处理解应该并不复杂,不在赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值