Django认证后端多个数据库

3
我正在重写一个传统应用程序,每个客户都有自己的数据库、验证和用户集。因此,我需要一个自定义的身份验证后端,因为Django的auth只能使用默认设置。我编写了中间件,它在每个请求时检查URL并提取信息以设置请求中的database_name。
如果我在自定义身份验证后端处理期间可以访问请求,我可以轻松地通过user = User.objects.using(request.db).get(username=username) 执行数据库调用。然而,我没有看到容易实现这一点的方法。我在这里看到了一个答案:Access request.session from backend.get_user,但这似乎不是线程安全的,所以我不想走这条路。
我唯一能看到仍然使用django-auth的解决方案是为每个客户创建一个身份验证后端,将要使用的数据库名称设置为类属性。然后,我会创建一个自定义登录函数,将request.session['_auth_user_backend']设置为特定于客户的后端。因此,在每个请求上调用get_user(user_id)时,它将使用知道从哪个数据库请求的客户后端。
如果可能,我想避免管理每个客户的身份验证后端。还有更好的方法吗?
2个回答

8
由于认证后端没有调用 QuerySet 方法 using,您可以使用带有数据库路由器和一些中间件的线程本地变量来设置变量为客户数据库名称。此中间件必须放置在身份验证中间件之前。
线程本地变量是线程安全的。它创建了一个线程本地全局变量。
如果您正在遵循请求的路径,则会执行以下操作:
  1. 请求到达 Django
  2. 您的自定义中间件从 URL 中获取数据库名称并将其设置为线程本地全局变量。
  3. Django 身份验证中间件启动并通过运行查询 User.object.get(id=user_id) 设置用户。这将使用您的数据库路由器,它将返回在前一个中间件中设置的线程本地全局变量。

  4. 请求继续进入 Django 堆栈的其余部分。

例如,您有以下模块:

my_app/middleware.py

from threading import local

my_local_global = local()

class CustomerMiddleware(object):
    def process_request(self, request):
        my_local_global.database_name = get_database_name(request)

my_app/routers.py

from middleware import my_local_global

class MultiCustomerRouter(object):
    def db_for_read(self, model, **hints):
        return my_local_global.database_name

settings.py

...
MIDDLEWARE_CLASSES = (
 'django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'my_app.middleware.CustomerMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
)

DATABASE_ROUTERS = ['my_app.routers.MultiCustomerRouter']
...

2

很可能您可以使用Django数据库路由器来控制这个问题。如果您知道哪个用户访问了哪个数据库,那么只需根据用户模型的逻辑定义即可。


路由器是一个有趣的想法。我以前用过它们,但我不认为它们会在调用def get_user(self, user_id):时对身份验证后端有所帮助。当这个调用发生时,我无法访问请求,只有一个ID可以使用。除非我入侵并完全更改后端,否则我怎么知道该指向哪里?我认为可能没有更好的方法。 :( - DivineSlayer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接