许多关于RESTful Web服务的示例没有考虑到当今许多应用程序都是多用户的问题。
想象一下,多用户后端暴露RESTful API 。后端数据体系结构使用共享数据库和共享模式。每个表都包含对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应该是什么样的?
以下是获取鲍勃宠物的方法的两种可能性:
可能性#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
答案 0 :(得分:4)
第1部分
(大声思考:你已经决定使用HTTP和HMAC吗?如果是这样,你为什么要问我们?)
我建议使用Basic Auth进行HTTPS。简单。毕竟,这对Stripe来说已经足够了。
参考文献:
更新:以下是有关如何处理身份验证的其他详细信息:
每个客户端应用程序都将使用API密钥与服务联系。使用HTTPS和Basic Auth,客户端将提供其API密钥作为基本auth用户名。它不需要提供密码,因为它使用的是HTTPS。您需要为每个应用程序(Web应用程序,Android,iOS)分配API密钥,我有两种方式:
一个。一种选择是为每个用户提供一个在客户端共享的API密钥。
B中。另一种选择是为每个客户提供一个独特的应用程序。)
但是,您如何获得客户的钥匙?构建“密钥请求”API端点。我建议给每个客户端一个“启动”密钥,该密钥仅用于联系该端点。 (启动密钥不授予其他访问权限。)当用户首次使用客户端时,他/她必须进行身份验证。客户端将其传递给“密钥请求”端点,以便它可以生成与用户关联的密钥。从那时起,每个客户端都有一个与客户端相关的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”中所读到的那样。
参考文献:
答案 1 :(得分:3)
通过“多租户”,您只是指应用程序/ Web服务用户?多租户往往意味着更复杂的msdn.microsoft.com/en-us/library/aa479086.aspx。
您需要使用您的网络服务对每个用户进行身份验证。这可以通过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。它还有一个关于设计资源以及如何管理用户和多个客户端的非常好的部分。
答案 2 :(得分:2)
我不确定这个问题,因为在这种情况下,多租户似乎有点矫枉过正。 但是我可以尝试回答第二个问题。
REST是一种“基于资源”的架构,您必须意识到/pets
和/pets/?tenant=1
不会引用相同的资源:
/pets
是指当前用户的宠物,/pets/?tenant=1
指鲍勃的宠物。虽然两种解决方案都没有错,但通常最好先获得第二种解决方案。 URI确实是为了共享而设计的,你有更多的理由来分享“鲍勃的宠物”(即使它需要身份验证和授权),而不是每个用户都不同的抽象“我的宠物”。
请参阅Should resource ids be present in urls?进行类似的讨论......
答案 3 :(得分:1)
如果HTTP是所有3种客户端类型使用的协议,您只需要实现一种身份验证方案。你可以挑选毒药 - Basic,Digest,Oauth2或Cookie是一些常用的方法。由于它是通过HTTP,我认为没有理由复制这个逻辑。可能有几个框架会根据您的平台为您抽象出来。
要区分客户端类型,可以使用HTTP标头。 user-agent
可能已经使这成为可能。另一种选择是使用您定义的自定义标头。任何HTTP客户端都能够设置标头,任何服务器都可以处理自定义标头。一个体面的Web框架将使您可以相对轻松地使用这些框架。作为后端服务,我怀疑您将要统一或尽可能地处理所有客户端请求。当然,维持一个后端优于三个。
就您的API应该是什么样子而言 - 完全取决于您。为了保持RESTful,how to GET a cup of coffee非常值得阅读 - 插入Roy Fielding's thesis的强制性链接。有可能,你真正想要的是编写“足智多谋”链接的指南。
在您列出的选项中,如果用户可能需要访问系统中的所有宠物,则首选/pets?userId=bob
。如果用户只需要访问他们的宠物,请首选/pets
。