如何防止用户更改URL <pk> 查看其他提交数据 Django
我刚刚进入网页开发的世界,开始学习Django,也在了解那些需要保护网址的应用程序,以防用户通过修改foo/bar/pk来访问其他用户的数据。
有没有办法防止这种情况发生?或者Django里有没有内置的方式来阻止这种事情?
比如说:foo/bar/22
可以被改成foo/bar/14
,这样就能看到其他用户以前的数据。
我看过很多关于这个话题的回答,但很少有能清楚明了地解释这个问题和防止方法的。我对这方面了解不多,所以不知道该怎么提问才能找到合适的答案。请像对五岁小孩一样给我解释一下。
8 个回答
你需要了解用户身份验证和授权,这两者都是由 Django的认证包 提供的。这两者之间有很大的区别。
身份验证就是确认某人是否真的是他们所说的那样。想象一下,登录的时候。你让用户输入他们的用户名和密码,以证明他们是这个账户的拥有者。
授权则是确认某人是否有权限访问他们想要访问的内容。比如说,普通用户就不能随便更改主键(PK)。
关于授权的内容在我提供的链接中有详细的说明。我建议你先从那里开始,看看一些示例代码。希望这能解答你的问题。如果没有,希望这些信息能帮助你更具体地提问。
在我的项目中,对于几个模型/表格,用户只能看到自己输入的数据,而看不到其他用户输入的数据。为了实现这一点,这些模型/表格中都有一个用户列。
在列表视图中,这个实现起来比较简单,只需要在传给列表视图的查询集中筛选出 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 模板,你就可以了。
只需要确认通过主键获取的对象是请求用户的。具体在视图中可以这样写:
if some_object.user == request.user:
...
这就需要表示这个对象的模型里有一个指向用户模型的引用。
如果你想要控制每个对象的访问权限,我建议你使用 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...
有几种方法可以实现这个功能:
如果你有登录的概念,可以把网址限制为:
/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
,如果没有登录则重定向到登录页面。