如何防止用户更改URL <pk> 查看其他提交数据 Django

5 投票
8 回答
4584 浏览
提问于 2025-04-18 17:18

我刚刚进入网页开发的世界,开始学习Django,也在了解那些需要保护网址的应用程序,以防用户通过修改foo/bar/pk来访问其他用户的数据。

有没有办法防止这种情况发生?或者Django里有没有内置的方式来阻止这种事情?

比如说:foo/bar/22可以被改成foo/bar/14,这样就能看到其他用户以前的数据。

我看过很多关于这个话题的回答,但很少有能清楚明了地解释这个问题和防止方法的。我对这方面了解不多,所以不知道该怎么提问才能找到合适的答案。请像对五岁小孩一样给我解释一下。

8 个回答

2

你需要了解用户身份验证和授权,这两者都是由 Django的认证包 提供的。这两者之间有很大的区别。

身份验证就是确认某人是否真的是他们所说的那样。想象一下,登录的时候。你让用户输入他们的用户名和密码,以证明他们是这个账户的拥有者。

授权则是确认某人是否有权限访问他们想要访问的内容。比如说,普通用户就不能随便更改主键(PK)。

关于授权的内容在我提供的链接中有详细的说明。我建议你先从那里开始,看看一些示例代码。希望这能解答你的问题。如果没有,希望这些信息能帮助你更具体地提问。

3

在我的项目中,对于几个模型/表格,用户只能看到自己输入的数据,而看不到其他用户输入的数据。为了实现这一点,这些模型/表格中都有一个用户列。

在列表视图中,这个实现起来比较简单,只需要在传给列表视图的查询集中筛选出 model.user = logged_id.user 的数据。

但是在详细信息/更新/删除视图中,看到网址中的主键(PK),用户可能会修改网址中的主键,从而访问到其他用户的数据。

我使用的是 Django 内置的基于类的视图。

那些在网址中有主键的视图已经使用了 LoginRequiredMixin,但这并不能阻止用户修改网址中的主键。

我的解决方案是:“当前登录用户是否拥有这一行数据的混合类”(DoesLoggedInUserOwnThisRowMixin)——重写 get_object 方法并在这里进行检查。

from django.core.exceptions import PermissionDenied

class DoesLoggedInUserOwnThisRowMixin(object):

    def get_object(self):
        '''only allow owner (or superuser) to access the table row'''
        obj = super(DoesLoggedInUserOwnThisRowMixin, self).get_object()
        if self.request.user.is_superuser:
            pass
        elif obj.iUser != self.request.user:
            raise PermissionDenied(
                "Permission Denied -- that's not your record!")
        return obj

就这样!

只需在视图类定义行中,把这个混合类放在 LoginRequiredMixin 后面,再加上一个输出消息的 403.html 模板,你就可以了。

3

只需要确认通过主键获取的对象是请求用户的。具体在视图中可以这样写:

if some_object.user == request.user: ...

这就需要表示这个对象的模型里有一个指向用户模型的引用。

4

如果你想要控制每个对象的访问权限,我建议你使用 django-guardian。下面是配置好设置并安装后,它的样子(这个内容来自 django-guardian的文档):

>>> from django.contrib.auth.models import User
>>> boss = User.objects.create(username='Big Boss')
>>> joe = User.objects.create(username='joe')
>>> task = Task.objects.create(summary='Some job', content='', reported_by=boss)
>>> joe.has_perm('view_task', task)
False

如果你不想使用外部库,Django的视图中也有方法可以实现这个功能,具体可以参考 Django的文档

下面是它可能的样子:

from django.http import HttpResponseForbidden
from .models import Bar

def view_bar(request, pk):
    bar = Bar.objects.get(pk=pk)
    if not bar.user == request.user:
        return HttpResponseForbidden("You can't view this Bar.")
    # The rest of the view goes here...
5

有几种方法可以实现这个功能:

如果你有登录的概念,可以把网址限制为:

/foo/bar/

然后在代码中,使用 user=request.user,只显示登录用户的数据。

另一种方法是:

/foo/bar/{{request.user.id}}/

然后在视图中:

def myview(request, id):
    if id != request.user.id:
        HttpResponseForbidden('You cannot view what is not yours') #Or however you want to handle this

你甚至可以写一个中间件,让用户被重定向到他们自己的页面 /foo/bar/userid,如果没有登录则重定向到登录页面。

撰写回答