Meteor 的安全系统不需要我们在每次修改数据的时候,在各自的函数里面进行手动检查。
例如,对于一个博客系统,我们常常需要做很多操作,往新帖子上添加属性,当发布帖子的时候进行特定检查。这些操作都是围绕帖子(post)这个对象进行的,所以我们应该为帖子设置一个专门的函数进行安全检查。
但在另一方面,我们又不希望为修改帖子或删除帖子这些简单的操作编写特定的函数。我们只需要在这些操作之前,检查用户是否有权限就可以了。这时我们就需要用到允许(allow)和拒绝(deny)回调函数。
这些回调函数可以方便地让我们定义哪些数据可以被修改,哪些可以被删除。另外,这些回调函数还整合了用户系统(account system),用以判断权限。
我们可以根据需要定义多个允许 allow
回调函数。但是我们只需要其中_至少有一个_返回 true
就可以让操作通过验证。当 Post.insert
被调用时(无论是在我们 app 的客户端代码调用,还是在浏览器控制台调用),服务器会逐个运行为 insert 设置的允许(allow)回调函数,直到其中一个返回 true
为止。如果这样的允许回调函数不存在,服务器就不会允许 insert 操作,并给客户端返回一个 403
错误。
类似地,我们也可以定义一个或者多个拒绝 deny
回调函数。如果其中一个回调函数返回 true
,操作就会被取消,并且返回 403
。这意味着,一个成功的 insert 操作要求至少一个返回 true
的允许 allow
回调函数,并且_所有_拒绝 deny
回调函数都返回 false
。
<%= diagram "allow_deny", "注:n/e 表示该函数没有被执行" %>
更直观地说,Meteor 从拒绝回调函数开始,然后是 allow
函数,逐一执行,直到有一个返回 true
。
这个模式的一个实际例子就是,建立两个 allow()
允许回调函数,一个用于检查帖子是否属于当前用户,另外一个用于检查当前用户是否管理员。如果当前用户是管理员,由于至少有一个回调函数返回 true
,那就确保他们可以更新任何帖子。
还记得我们前面说过,数据库的可变函数(例如 .update()
)使用了一种延迟补偿技术。所以,当你尝试从浏览器的控制台删除一篇不属于你的帖子的时候,你会看到帖子短时间内消失,但马上又会重新出现。这是因为帖子并没有真正从后台删除,删除操作被后台拒绝了。
这样的行为在控制台上不是问题(开发者可以随意支配数据进行开发)。但是,你需要确保这样的行为不会出现在用户界面上。比如说,你需要确保对于用户不能删除的帖子,用户不会看到删除键。
庆幸的是,你可以在客户端和服务端使用一样的代码进行权限管理(例如,你可以写一个 canDeletePost(user, post)
函数,把它放在一个共享的 /lib
文件夹下面),这样做通常不需要太多额外的代码。
只有来自客户端的数据库操作需要被权限系统验证。服务端的_所有_操作都被认定为安全的,不需要被权限系统验证。
这意味着如果你创建了一个服务端函数 deletePost
,而且这个函数可以被客户端执行,那么任何用户都可以删除任何帖子了。因此,你可能不想那么做,除非你在函数中也验证用户权限。