我有一个Django应用程序。以管理员用户身份登录时,我希望能够在URL中传递一个秘密参数,并使整个网站的行为就像我是另一个用户一样。
假设我有一个显示当前登录用户个人资料的网址/my-profile/
。我希望能够执行/my-profile/?__user_id=123
之类的操作并让底层视图相信我实际上是ID为123的用户(因此呈现 用户的个人资料)。
我为什么要这样?
只是因为重现某些只出现在单个用户帐户中的错误要容易得多。
我的问题:
实现这样的最简单方法是什么?
这样做时我是否应该考虑安全问题?请注意,我(显然)只想为管理员用户提供此功能,而且我们的管理员用户无论如何都可以完全访问源代码,数据库等,所以它不是真正的“后门”;它只是让更容易访问用户的帐户。
答案 0 :(得分:33)
我没有足够的声誉来编辑或回复(我认为),但我发现虽然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
答案 1 :(得分:9)
我用一个简单的中间件解决了这个问题。它还处理重定向(即,在重定向期间保留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
答案 2 :(得分:9)
看起来很多其他人都遇到过这个问题并且已经编写了可重复使用的应用程序来执行此操作,至少有一些列在django packages page for user switching上。写作时最活跃的似乎是:
答案 3 :(得分:2)
我不知道这是一个安全漏洞,而不是在一台unix机器上使用su - someuser作为root用户。 root或具有root / admin访问权限的django-admin可以伪造任何内容,如果他/她想要的话。风险仅在于django-admin帐户被破解,此时破解者可以通过成为另一个用户隐藏跟踪然后伪造用户的行为。
是的,它可能被称为后门,但正如ibz所说,管理员无论如何都可以访问数据库。能够在这种情况下对数据库进行更改也是一个后门。
答案 4 :(得分:1)
@Charles Offenbacher的答案非常适合冒充未通过令牌进行身份验证的用户。但是,它不适用于使用令牌身份验证的客户端应用程序。要让用户模拟使用令牌使用应用程序,必须直接在模拟中间件中设置HTTP_AUTHORIZATION标头。我的回答基本上抄袭了查尔斯的回答,并添加了手动设置所述标题的行。
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'])
# retrieve user's token
token = Token.objects.get(user=request.user)
# manually set authorization header to user's token as it will be set to that of the admin's (assuming the admin has one, of course)
request.META['HTTP_AUTHORIZATION'] = 'Token {0}'.format(token.key)
答案 5 :(得分:-1)
设置,以便您有两个不同的主机名到同一台服务器。如果您在本地执行此操作,则可以使用127.0.0.1或localhost进行连接。您的浏览器会将此视为三个不同的站点,您可以使用不同的用户登录。这同样适用于您的网站。
因此除了www.mysite.com之外,您还可以设置test.mysite.com,并在那里与用户一起登录。我经常设置网站(使用Plone)所以我同时拥有www.mysite.com和admin.mysite.com,并且只允许从那里访问管理页面,这意味着我可以使用具有的用户名登录普通网站问题。