7

很多关于 RESTful Web 服务的例子都没有考虑到当今许多应用程序都是多用户的问题。

想象一个公开 RESTful API 的多用户后端。后端数据架构使用共享数据库和共享模式。每个表都将包含对 a 的引用tenant_id

+-------------+----+-----------------+
|  tenant_name| id |   shared_secret |
+-------------+----+-----------------+
|         bob |  1 |   2737sm45sx543 |
+-------------+----+-----------------+
|       alice |  2 |   2190sl39sa8da |
+-------------+----+-----------------+

+-------------+----+-------+-----------+
|    pet_name | id |  type | tenant_id |
+-------------+----+-------+-----------+
|       fuffy |  1 |   dog |         1 |
+-------------+----+-------+-----------+
|       kerry |  2 |   cat |         2 |
+-------------+----+-------+-----------+

问题 1三个或更多客户端应用程序(即 Android、iOS 和 Web App)与 RESTful 后端交互,您将如何针对后端执行身份验证?

RESTful backend, API, HTTP-Verbs, shared database and schema 
|
|
+---- Web Application (Client 1)
|     |
|     + Alice
|     |
|     + Bob
|
+---- Android Application (Client 2)
|     |
|     + Alice
|     |
|     + Bob
|
+---- iOS Application (Client 3)
|     |
|     + Alice
|     |
|     + Bob
|

每个客户都应该允许 Alice 和 Bob 管理她/他的宠物。每个客户端都是一个 GUI,它将使用(在内部,发出 HTTP 请求)后端。问题:每个客户端如何才能对后端进行身份验证?

假设 HMAC(它完全是 RESTful,没有会话):此方法涉及使用共享密钥对有效负载进行签名(从不通过网络发送)。每个客户是否应该拥有自己的tenant表格副本(包含该shared_secret字段)?

Android App -> Client Sign -> Signed Request -> Backend -> Result
    Web App -> Client Sign -> Signed Request -> Backend -> Result

问题 2:资源 URI 应该是什么样的?

以下是获取 Bob 宠物的两种可能性:

可能性 #1:Authorization标题为您提供租户的(唯一)名称:

GET /pets HTTP/1.1
Host: www.example.org
Authorization: bob:c29kYW9kYSBhb2lzYWRoIGYgZDUzNDUz

可能性 #2。作为tenant_id查询参数发送:

GET /pets/tenant_id=1 HTTP/1.1
Host: www.example.org
Authorization: bob:c29kYW9kYSBhb2lzYWRoIGYgZDUzNDUz
4

4 回答 4

4

第1部分

(大声思考:您是否已经决定使用 HTTP 和 HMAC?如果是,您为什么要问我们?)

我建议使用带有基本身份验证的 HTTPS。简单的。毕竟对于Stripe来说已经足够好了。

参考:

更新:以下是有关如何处理身份验证的一些其他详细信息:

  1. 每个客户端应用程序都将使用 API 密钥联系服务。使用 HTTPS 和基本身份验证,客户端将提供其 API 密钥作为基本身份验证用户名。它不需要提供密码,因为它使用的是 HTTPS。您需要为每个应用程序(Web 应用程序、Android、iOS)分配一个 API 密钥,我看到两种方法:

    A. 一种选择是为每个用户提供一个跨客户端共享的 API 密钥。

    B. 另一种选择是为每个客户提供一个独特的应用程序。)

  2. 但是,您首先如何获得客户的钥匙呢?构建“密钥请求”API 端点。我建议给每个客户端一个“starter”键,它只用于联系这个端点。(启动密钥不授予其他访问权限。)当用户第一次使用客户端时,他/她必须进行身份验证。客户端将此传递给“密钥请求”端点,以便它可以生成与用户关联的密钥。从那时起,每个客户端都有一个客户端绑定的 API 密钥。

第2部分

考虑给每个租户一个子域。如果您使用 Rails(或任何现代 Web 堆栈),您可以使用子域来查找租户 ID。然后你的 API 可以这样使用:

GET http://tenant1.app.co/pets
GET http://tenant2.app.co/pets
GET http://tenant3.app.co/pets

参考资料(特定于 Rails,但应该对 Web 堆栈有所帮助):

注意:正如您的示例所示,为简单起见,我不会为不同的租户重复使用相同的宠物 ID。例如,以下是一个简单的方法:

GET http://tenant1.app.co/pets/200
GET http://tenant2.app.co/pets/201
GET http://tenant3.app.co/pets/202

我描述的方法比tenant_id作为查询参数传递要干净得多。此外,使用tenant_id作为参数感觉不对。正如我在 Ruby 和 Richardson 的“RESTful Web Services”中所读到的,我喜欢将参数用于更多“算法”的东西。

参考:

于 2013-03-08T03:01:06.993 回答
3

您所说的“多租户”是指应用程序/Web 服务用户吗?多租户通常意味着更复杂的东西msdn.microsoft.com/en-us/library/aa479086.aspx

您需要使用您的 Web 服务对每个用户进行身份验证。这可以通过 SSL 上的基本 http 身份验证来完成。

从 Web 服务的角度来看,您将对所有三个客户端执行相同的身份验证。该服务不关心客户的类型 - 这就是重点。您可能需要为您的客户端提供不同的表示形式,例如 XHTML 或 JSON。我喜欢保持简单,总是选择 JSON。

对于资源,管理它们的最简单方法是将用户资源作为顶层,然后为每个用户将所有资源链接在一起,例如

GET users/fred/pets - returns all pets for user fred
GET users/fred/pets/sparky - returns details on freds pet sparky

这样做的好处是您可以添加代码来授权每个请求,例如您可能有两个用户,fred 和 jack。两个用户都将通过身份验证,但您应该只允许 fred 请求他的资源,而让 jack 请求他的资源。您只需在您的 api 中添加授权检查,例如从 URI 获取用户名,获取经过身份验证的用户的用户名,检查它们是否相同。如果不返回类似 http 403 禁止的内容,如果它们相同,则允许请求。

我认为如果您仍然不清楚,您需要阅读 REST 的详细信息。到目前为止,关于这个主题的最好的书是这本RESTful Web Services。它从第一原则涵盖了 REST。它也有一个非常好的部分关于设计资源,以及如何管理用户和多个客户端。

于 2013-03-04T17:03:33.170 回答
2

我不确定是否理解这个问题,因为在这种情况下,多租户似乎有点矫枉过正。但是我可以尝试回答第二个问题。

REST 是一种“基于资源”的架构,您必须意识到这一点/pets并且/pets/?tenant=1不引用相同的资源:

  • /pets指当前用户的宠物,
  • /pets/?tenant=1指鲍勃的宠物。

虽然两种解决方案都没有错,但通常最好获得第二种解决方案。URI 确实是为共享而设计的,与每个用户不同的抽象“我的宠物”相比,您有更多理由分享“鲍勃的宠物”(即使它需要身份验证和授权来表示)。

请参阅URL 中是否应该存在资源 ID?进行类似的讨论...

于 2013-03-04T19:04:09.467 回答
1

如果 HTTP 是所有 3 种客户端类型都使用的协议,那么您应该只需要实现一种身份验证方案。您可以选择毒药 - BasicDigestOauth2Cookie是一些常用的方法。由于它通过 HTTP,我认为没有理由复制此逻辑。根据您的平台,可能有几个框架会为您抽象这个。

要区分客户端类型,可以使用 HTTP 标头user-agent可能已经使这成为可能。另一种选择是使用您定义的自定义标题。任何 HTTP 客户端都可以设置标头,任何服务器都可以处理自定义标头。一个体面的 Web 框架将使您可以相对轻松地使用这些框架。作为后端服务,我怀疑您会希望统一处理所有客户端请求 - 或尽可能多地处理。当然,维护一个后端优于三个

至于你的 API 应该是什么样子 - 这完全取决于你。为了保持 RESTful,如何获得一杯咖啡非常值得阅读 - 插入Roy Fielding 论文的必填链接。很有可能,您真正需要的是编写“资源丰富”链接的指南。

/pets?userId=bob在您列出的选项中,如果用户可能需要访问系统中的所有宠物,则更喜欢第二个选项。/pets如果用户只需要访问他们的宠物,则更喜欢第一个。

于 2013-03-08T02:42:01.853 回答