Beginner’s Guide

This is an attempt to bring together a number of concepts in python-social-auth (PSA) so that you will understand how it fits into your system. This definitely has a Django flavor to it (because that’s how I learned it).

Понимание URL-адресов PSA

If you have not seen namespaced URLs before, you are about to be introduced. When you add the PSA entry to your urls.py, it looks like this:

url(r'', include('social_django.urls', namespace='social'))

часть «namespace» на конце - это то, что удерживает имена в PSA-мире от столкновения с именами в вашем приложении или других сторонних приложениях. Таким образом, ваша ссылка для входа будет выглядеть так:

<a href="{% url 'social:begin' 'provider-name' %}">Login</a>

(See how «social» in the URL mapping matches the value of «namespace» in the urls.py entry?)

Понимание бэкендов

В PSA реализовано множество бэкендов. Найдите запись в документации для вашего бэкенда, и если она там есть, выполните следующие шаги для включения, которые сводятся к следующему

  1. Настройте переменные SOCIAL_AUTH_{backend} в файле settings.py. (Настройки варьируются в зависимости от бэкенда)

  2. Adding your backend to AUTHENTICATION_BACKENDS in settings.py.

Если вам нужно реализовать другой бэкэнд (например, допустим, вы хотите использовать OpenID от Intuit), вы можете подклассифицировать ближайший и переопределить атрибут «name»:

from social_core.backends.open_id import OpenIDAuth

class IntuitOpenID(OpenIDAuth):
    name = 'intuit'

А затем добавьте ваш новый бэкенд в AUTHENTICATION_BACKENDS в settings.py.

Несколько замечаний о трубопроводе:

Стандартный конвейер регистрирует пользователя только после завершения работы конвейера. Поэтому если вы получаете значение в ключе user накопительного словаря, это означает, что пользователь вошел в систему в момент начала процесса.

Понимание трубопровода

Перевернув URL, например {% url 'social:begin' 'github' %}, вы получите URL, например:

http://example.com/login/github

Щелчок по этой ссылке приведет к запуску «конвейера». Конвейер - это список функций, которые накапливают данные о пользователе по мере прохождения этапов процесса аутентификации. (Если вы действительно хотите понять, что такое конвейер, посмотрите исходный текст в social/backends/base.py, а функцию run_pipeline() - в BaseAuth).

Контракт на проектирование для каждой функции в трубопроводе является:

  1. Конвейер начинается со словаря из четырех элементов (накопительный словарь), который обновляется результатами каждой функции в конвейере. Начальные четыре значения таковы:

    strategy

    содержит объект стратегии

    backend

    содержит бэкенд, используемый во время запуска данного трубопровода

    request

    содержит словарь ключей запроса. Примечание для пользователей Django - это не объект HttpRequest, а результат выполнения команды request.REQUEST.

    details

    который является пустым диктом.

  2. Если функция возвращает словарь или что-то False-ish, добавьте содержимое словаря в накопительный словарь (называемый out в run_pipeline), и вызовите следующий шаг конвейера с накопительным словарем.

  3. Если возвращается что-то другое (например, подкласс HttpResponse), то верните это браузеру.

  4. Если конвейер завершается, ТОГДА пользователь аутентифицирован (вошел в систему). Поэтому если вы находите объект аутентифицированного пользователя во время работы конвейера, это означает, что пользователь вошел в систему во время запуска конвейера.

Существует один конвейер для сайта в целом - если у вас есть логика, специфичная для бэкенда, вы должны сделать шаги вашего конвейера достаточно умными, чтобы пропустить шаг, если он не релевантен. Это просто:

def my_custom_step(strategy, backend, request, details, *args, **kwargs):
    if backend.name != 'my_custom_backend':
        return
    # otherwise, do the special steps for your custom backend

Прерывание конвейера (и взаимодействие с представлениями)

Допустим, вы хотите добавить пользовательский шаг в конвейер - вы хотите, чтобы пользователь установил пароль, чтобы в будущем он мог зайти непосредственно на ваш сайт. Мы можем сделать это с помощью декоратора @partial, который указывает конвейеру отслеживать, где он находится, чтобы его можно было перезапустить.

Первое, что нам нужно сделать, это установить способ взаимодействия наших представлений с конвейером. Это делается путем добавления значения в файл настроек, чтобы указать нам, какие значения должны передаваться туда и обратно между сессией и конвейером:

SOCIAL_AUTH_FIELDS_STORED_IN_SESSION = ['local_password',]

В нашем коде трубопровода мы бы имели:

from django.shortcuts import redirect
from django.contrib.auth.models import User
from social_core.pipeline.partial import partial

# partial says "we may interrupt, but we will come back here again"
@partial
def collect_password(strategy, backend, request, details, *args, **kwargs):
    # session 'local_password' is set by the pipeline infrastructure
    # because it exists in FIELDS_STORED_IN_SESSION
    local_password = strategy.session_get('local_password', None)
    if not local_password:
        # if we return something besides a dict or None, then that is
        # returned to the user -- in this case we will redirect to a
        # view that can be used to get a password
        return redirect("myapp.views.collect_password")

    # grab the user object from the database (remember that they may
    # not be logged in yet) and set their password.  (Assumes that the
    # email address was captured in an earlier step.)
    user = User.objects.get(email=kwargs['email'])
    user.set_password(local_password)
    user.save()

    # continue the pipeline
    return

В коде представления у нас будет что-то вроде:

class PasswordForm(forms.Form):
    secret_word = forms.CharField(max_length=10)

def get_user_password(request):
    if request.method == 'POST':
        form = PasswordForm(request.POST)
        if form.is_valid():
            # because of FIELDS_STORED_IN_SESSION, this will get copied
            # to the request dictionary when the pipeline is resumed
            request.session['local_password'] = form.cleaned_data['secret_word']

            # once we have the password stashed in the session, we can
            # tell the pipeline to resume by using the "complete" endpoint
            return redirect(reverse('social:complete', args=("backend_name,")))
    else:
        form = PasswordForm()

    return render(request, "password_form.html")

Обратите внимание, что social:complete снова войдет в конвейер с той же функцией, которая его прервала (в данном случае, collect_password).

Вернуться на верх