2016-08-02 62 views
-1

我们正在开发使用Azure DocumentDb作为存储的多租户Angular Web App。来自应用程序所有租户的文档存储在同一个集合中,并由文档的tenantId属性进行区分。我们将拥有中间层.NET服务,该服务持有DocumentDb的主密钥并公开REST API端点以便通过Web App进行CRUD操作。 Web App的用户通过oAuth提供商(Google,Facebook,Microsoft)进行身份验证。确保租户数据的最佳做法是什么,以便一个租户的用户无法访问其他租户的数据?确保从多租户SPA访问DocumentDB

+0

不幸的是,这种类型的问题对于StackOverflow是无关紧要的,因为没有一种“最佳实践”方式可以实现这一点。这是一个相当广泛和意见征求的问题,可能有很多答案。 –

回答

1

我没有足够的信心把它最好的做法,但我们有一个中间层REST API像你一样,在这里我们做什么:

  1. 客户端(甚至浏览器代码)可以提交任意查询。我们使用sql-from-mongo,所以这些语言都是类似于mongo的语法,这对于javascript客户端来说更容易构建查询,但是我建议使用原始SQL查询的安全性一样安全,只要您将变量参数化并且禁止您的SELECT条款中的任何投影。这是我们在sql-from-mongo翻译中完成的最后一部分,但是您可以删除预测(将提供的SELECT子句替换为SELECT *),或拒绝任何不以SELECT *开头的查询。这是非常重要的,因为如果没有它,不好的演员可以投射他们自己的tenentIDuserID,这些控制的其余部分不起作用。

  2. 我们的REST中间件为每个客户端缓存租户和用户上下文,这些包含我们的授权规范(类似于Firebase的概念)。用最简单的形式,用户绑定到单个帐户,并且该用户已读取该租户的所有数据的权限,但您可以指定任何内容。

  3. 执行查询时,将根据授权规范检查返回的数据集。根据具体情况,任何数量的禁止文档的请求都会被完全拒绝,或者不允许的文档会从我们返回的结果集中过滤掉。

  4. 在写事物的方面,我们做了类似的事情。我们根据授权规范检查更新并拒绝任何不符合规定的更新。这比这更复杂一点,因为我们在所有写入中使用sproc,并且我们为就地更新使用类似mongo的语法,并且我们为所有写入(使用sproc)实现了跨文档ACID,但是您可以执行我没有这些建议。

希望这会有所帮助。随意在评论中提问。

+0

拉里,谢谢你的详细回复。我打算做的是: 1.使用oAuth提供程序登录后,客户端将oAuth提供程序ID发送到我们的Auth服务器。 2.Auth服务器从我们的数据库中获取相应的tenantId和userRole。 3.Auth服务器使用tenantId和userRole生成JWT并将其返回给客户端。 4.客户端将JWT包含到HTTP授权标头中,用于对我们的REST API服务的每个请求 –

+1

我不建议您使用oauth标记作为密钥并仅将服务器缓存在服务器上将令牌传递给/从客户端。否则,客户可能会欺骗这些东西。 –

+1

或者,不要将其缓存在服务器上,而是在每个请求上查找授权信息。效率会降低,但可以让服务器保持无状态,并可能避免严重的缓存失效问题。 –

2

这可以通过在读取&写入时在逻辑上截取中间层中的请求以及在每个文档中保留tenantId标记来实现。

让我们从读取路径开始。假设每个文档的tenantId属性都设置为该文档所属用户的相应租户。要读取任何数据,请通过参数化SQL查询(https://azure.microsoft.com/en-us/blog/announcing-sql-parameterization-in-documentdb/)并始终确保查询具有tenantId过滤器。这将确保用户的请求仅处理请求应该查看的租户ID。参数化对于避免注入以及访问其他租户的数据非常重要。

在写入路径上,您需要确保每个文档都具有正确设置的tenantId属性。如果最终客户端没有设置,或者甚至没有设置,则中间层应该解析并确保它与从OAuth提供程序返回的用户授权令牌对应的租户相匹配。

在此上下文中,请注意tenantId的分区键将有助于将单个租户的所有数据放在一起。只要查询具有tenantId过滤器,这对于在单个分区上进行置零方面的高效查询很有帮助。