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 реализовано множество бэкендов. Найдите запись в документации для вашего бэкенда, и если она там есть, выполните следующие шаги для включения, которые сводятся к следующему
Настройте переменные SOCIAL_AUTH_{backend} в файле settings.py. (Настройки варьируются в зависимости от бэкенда)
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
).
Контракт на проектирование для каждой функции в трубопроводе является:
Конвейер начинается со словаря из четырех элементов (накопительный словарь), который обновляется результатами каждой функции в конвейере. Начальные четыре значения таковы:
strategy
содержит объект стратегии
backend
содержит бэкенд, используемый во время запуска данного трубопровода
request
содержит словарь ключей запроса. Примечание для пользователей Django - это не объект HttpRequest, а результат выполнения команды
request.REQUEST
.details
который является пустым диктом.
Если функция возвращает словарь или что-то False-ish, добавьте содержимое словаря в накопительный словарь (называемый
out
вrun_pipeline
), и вызовите следующий шаг конвейера с накопительным словарем.Если возвращается что-то другое (например, подкласс
HttpResponse
), то верните это браузеру.Если конвейер завершается, ТОГДА пользователь аутентифицирован (вошел в систему). Поэтому если вы находите объект аутентифицированного пользователя во время работы конвейера, это означает, что пользователь вошел в систему во время запуска конвейера.
Существует один конвейер для сайта в целом - если у вас есть логика, специфичная для бэкенда, вы должны сделать шаги вашего конвейера достаточно умными, чтобы пропустить шаг, если он не релевантен. Это просто:
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).