Пользователи Django и аутентификация из внешнего источника
У меня есть приложение Django, которое получает его полностью из внешнего источника (запрашивается через HTTP). То есть у меня нет возможности для локальной базы данных. Данные сеанса хранятся в кеше (на моем сервере разработки я использую базу данных SQLite, поэтому это не источник ошибок). Я использую кровоточащий край Django 1.1svn.
Введите проблему: я хочу использовать собственную систему аутентификации Django для пользователей.
Кажется довольно простым написать мой собственный Authentication Backend, но всегда просто при условии, что у вас есть локальная база данных, где можно сохранить пользователей. Без базы данных моя главная проблема - настойчивость.
Я попробовал это со следующим (предположим, что datasource.get()
- это функция, которая возвращает какой-то dict):
class ModelBackend (object):
"""Login backend."""
def authenticate (self, username=None, password=None):
"""Check, if a given user/password combination is valid"""
data = datasource.get ('login', username, password)
if data and data['ok']:
return MyUser (username=username)
else:
raise TypeError
return None
def get_user (self, username):
"""get data about a specific user"""
try:
data = datasource.get ('userdata', username)
if data and data['ok']:
return data.user
except:
pass
return None
class MyUser (User):
"""Django user who isn't saved in DB"""
def save (self):
return None
Но намеренно отсутствующий метод save()
на MyUser, похоже, нарушает хранение сеанса входа в систему.
Как выглядеть MyUser
без локальной базы данных?
Ответы
Ответ 1
Хорошо, это намного сложнее, чем я думал. Сначала начните с http://docs.djangoproject.com/en/dev/howto/auth-remote-user/, но вам нужно будет расширить его с помощью собственного бэкэнд и пользователя.
from django.contrib.auth.backends import RemoteUserBackend
class MyRemoteUserBackend (RemoteUserBackend):
# Create a User object if not already in the database?
create_unknown_user = False
def get_user (self, user_id):
user = somehow_create_an_instance_of (MyUser, user_id)
return user
def authenticate (self, **credentials):
check_credentials ()
user = somehow_create_an_instance_of (MyUser, credentials)
return user
Затем пользователь:
from django.contrib.auth.models import User
class MyUser (User):
def save (self):
"""saving to DB disabled"""
pass
objects = None # we cannot really use this w/o local DB
username = "" # and all the other properties likewise.
# They're defined as model.CharField or similar,
# and we can't allow that
def get_group_permissions (self):
"""If you don't make your own permissions module,
the default also will use the DB. Throw it away"""
return [] # likewise with the other permission defs
def get_and_delete_messages (self):
"""Messages are stored in the DB. Darn!"""
return []
Уф! Django действительно не предназначен для использования без базы данных...
Ответ 2
Вместо того, чтобы перезаписывать метод save
, вы также можете отключить сигнал, который его вызывает. Это то, что я делаю в некоторых приложениях, которые имеют доступ только для чтения к пользовательской базе данных.
# models.py (for instance)
from django.contrib.auth.models import update_last_login, user_logged_in
user_logged_in.disconnect(update_last_login)
Ответ 3
grep
ping источник показал, что на самом деле вызывается единственное место user.save()
(за исключением кода создания пользователя и пароля, который вам вообще не нужно использовать) django.contrib.auth.login()
, для обновления user.last_login
.
# TODO: It would be nice to support different login methods, like signed cookies.
user.last_login = datetime.datetime.now()
user.save()
Если вы не хотите, чтобы данные пользователя оставались в БД, попробуйте добавить фиктивный метод save()
. Если я прав, он должен работать.
def save(self, *args, **kwargs):
pass
Конечно, поскольку у вас нет настойчивости вообще, вам следует рассмотреть результаты кэширования datasource.get
, иначе в худшем случае вы можете снова и снова запрашивать данные каждый раз, когда каждый зарегистрированный пользователь ударил.