用中间件覆盖Django身份验证

时间:2012-11-12 00:49:53

标签: django authentication django-middleware

我有一个Django网站和一个MyBB论坛,我想在他们之间共享身份验证。我的网站曾经是留言板;然后我在Django中构建了一些其他部分,MyBB和Django都运行在同一个域中。我已经建立了一个系统,在注册时(在论坛上)每个用户都有两个用户:一个Django用户和一个MyBB用户。用户使用论坛登录,因此我需要Django来读取MyBB的cookie并将相应的Django帐户设置为登录用户。

我可以用中间件做到吗?该中间件将读取MyBB的cookie(其中包含MyBB用户的id)并将request.user设置为相应的Django用户。我是Django的新手,我不确定在中间件中设置request.user(或调用authenticate)是个好主意(或者是否有更好的方法)。

4 个答案:

答案 0 :(得分:6)

如果存储在MyBB cookie中的user_id代表Django数据库中的同一用户,那么您可以使用默认的Django后端直接从该ID获取用户对象。如果这些ID不匹配,则需要自定义后端来获取Django用户对象。 要从MyBB cookie获取用户ID并根据它更新用户,您需要拥有自定义身份验证中间件。

<强>中间件

主要思想是获取用户对象(基于您的身份验证逻辑)并将其分配给request.user。这是一个例子(未经测试)。

from django.contrib import auth

class MyBBMiddleware:
    def process_request(self, request):
        user_cookie_name = "session_key"
        if user_cookie_name not in request.COOKIES:
            # log user out if you want
            return 
        id = request.COOKIES.get(user_cookie_name)
        # this will find the right backend
        user = auth.authenticate(id) 
        request.user = user
        # if you want to persist this user with Django cookie do the following
        #auth.login(request, user)

请记住,每次发送到Django站点的请求都会调用此方法。为了提高性能,您可以缓存用户和/或执行惰性对象技巧EXAMPLE

<强>后端

如果您需要编写自己的逻辑来获取用户对象并验证用户,您可以执行以下操作。

class MyBBCookieBackend(object):
    def authenticate(self, user_id):
        return self.get_user(user_id)
    def get_user(self, user_id):
        # if user_id is not the same in Django and MyBB tables, 
        #  you need some logic to relate them and fetch Django user
        try:
            #TODO your custom logic
            user = User.objects.get(id=user_id)
            return user
        except User.DoesNotExist:
            return None

您需要在站点设置文件中添加自定义后端和中间件。

答案 1 :(得分:4)

我认为正确的做法是将中间件和Django authentication backend组合在一起。

您的中间件将使用用户ID作为关键字参数调用后端的authenticate()。您的身份验证后端将依次调用相应的authenticate()方法并返回用户对象。

class MiddlewareTracker:
 def process_request(self, request):
    id = request.COOKIES.get('logged_in_id') 
    authenticate(user_id = id)
    return None

class ForumAuthBackend(object):

    def authenticate(self, *args, **kwargs):
        id = kwargs.get('user_id')
        return User.objects.get(id = id)

    def get_user(self, user_id):
        return User.objects.get(id = user_id)

我还建议您完成此https://docs.djangoproject.com/en/1.2/topics/auth/

答案 2 :(得分:0)

尝试处理request.sessionrequest对象作为视图中的第一个参数出现。有关会话的更多详细信息:https://docs.djangoproject.com/en/dev/topics/http/sessions/

您绝对可以使用中间件:有关中间件的更多信息,请访问:https://docs.djangoproject.com/en/dev/topics/http/middleware/

答案 3 :(得分:0)

我认为使用process_view代替process_request会更好,因为流程视图具有以下信息:

  1. 请求
  2. 要调用的函数
  3. 传递参​​数
  4. process_view(request, view_func, view_args, view_kwargs):
        if not request.user.is_authenticated():
            return HttpResponseRedirect(settings.LOGIN_URL)
        return None
    

    这使得它更灵活,因为我们也有函数information(view_func)。 所以我们也可以通过使用:

    将其限制为某些功能
    view_func.__name_
    

    给出了函数名称。