Django 管理员用户假冒

29 投票
8 回答
12579 浏览
提问于 2025-04-15 19:08

我有一个Django应用。当我以管理员身份登录时,我想在网址中传递一个秘密参数,让整个网站表现得像我是在以另一个用户的身份浏览。

比如说,我有一个网址 /my-profile/,这个页面显示的是当前登录用户的个人资料。我希望能够像这样使用 /my-profile/?__user_id=123,让系统认为我实际上是ID为123的用户(这样就能显示出那个用户的个人资料)。

我为什么想这样做呢?

因为这样可以更方便地重现一些只在某个用户账户中出现的bug。

我有几个问题:

  1. 实现这样的功能最简单的方法是什么?

  2. 在做这个的时候,有没有什么安全方面的考虑?请注意,我(显然)只想让管理员用户使用这个功能,而我们的管理员用户本来就可以完全访问源代码、数据库等,所以这并不算是一个“后门”;它只是让访问用户账户变得更简单

8 个回答

15

看起来很多人都遇到过这个问题,并且他们写了一些可以重复使用的应用程序来解决这个问题,至少有一些在Django用户切换的包页面上列出了。到目前为止,最活跃的几个应用程序包括:

  • django-hijack 在管理员的用户列表中放了一个“劫持”按钮,同时在页面顶部显示你正在劫持某个账户的提示。
  • impostor 允许你用用户名“我作为其他人”和自己的密码登录。
  • django-impersonate 设置了一些网址,可以开始模仿某个用户、停止模仿、搜索等等。
42

我还没有足够的声望来编辑或回复(我想是这样),但我发现虽然ionaut的解决方案在简单情况下有效,但对我来说,更稳妥的办法是使用会话变量。这样,即使是AJAX请求也能正确处理,而不需要修改请求的URL来添加一个GET伪装参数。

class ImpersonateMiddleware(object):
    def process_request(self, request):
        if request.user.is_superuser and "__impersonate" in request.GET:
            request.session['impersonate_id'] = int(request.GET["__impersonate"])
        elif "__unimpersonate" in request.GET:
            del request.session['impersonate_id']
        if request.user.is_superuser and 'impersonate_id' in request.session:
            request.user = User.objects.get(id=request.session['impersonate_id'])

用法:

log in: http://localhost/?__impersonate=[USERID]
log out (back to admin): http://localhost/?__unimpersonate=True
11

我用一个简单的中间件解决了这个问题。它还可以处理重定向(也就是说,在重定向过程中,GET参数会被保留)。下面是代码:

class ImpersonateMiddleware(object):
    def process_request(self, request):
        if request.user.is_superuser and "__impersonate" in request.GET:
            request.user = models.User.objects.get(id=int(request.GET["__impersonate"]))

    def process_response(self, request, response):
        if request.user.is_superuser and "__impersonate" in request.GET:
            if isinstance(response, http.HttpResponseRedirect):
                location = response["Location"]
                if "?" in location:
                    location += "&"
                else:
                    location += "?"
                location += "__impersonate=%s" % request.GET["__impersonate"]
                response["Location"] = location
        return response

撰写回答