REST API登录模式

199

我正在创建一款REST API,严格遵循apigee建议,使用名词而非动词,将API版本嵌入到URL中,每个集合使用两个API路径,GET POST PUT DELETE用法等。

我正在处理登录系统,但不确定正确的REST方式来登录用户。目前我还没有处理安全性,只是在处理登录模式或流程。(稍后我们将添加2步oAuth,使用HMAC等)

可能的选项:

  • POST请求到类似于https://api...com/v1/login.json的地址
  • PUT请求到类似于https://api...com/v1/users.json的地址
  • 其他我未考虑到的方式...

如何以符合REST风格的方式登录用户?


9
这是响应格式。".json"告诉服务器用json响应,".xml"则告诉服务器用xml格式响应。而不是将其作为可选参数放在“?”后面。http://blog.apigee.com/detail/restful_api_design_support_multiple_formats - Scott Roepnack
28
我从未见过在URL上进行内容协商,只有在头部进行。在URL上这意味着你失去了缓存和其他更多的好处。 - Oded
10
如果您考虑“接受”HTTP标头,@ScottRoepnack。 - Alessandro Vendruscolo
2
@Oded 如果您使用了“Accept”标头,那么您还会有一个“Vary:Accept”,因此缓存不会受到影响。关于扩展名的内容协商之前已经进行过讨论;我同意Shonzilla在那里的答案。 - cmbuckley
2
@Oded - 我不明白。如果您在URL中指定内容类型(无论是作为查询路径的.json后缀还是作为type=json查询参数),为什么会失去缓存的好处呢?而“您”在这种情况下是谁?谁会失去缓存的好处?在我看来,任何查询的结果都可以被缓存,无论查询路径或参数中包含什么。 - Cheeso
显示剩余5条评论
3个回答

149

现代Web架构的原则设计,作者为Roy T. Fielding和Richard N. Taylor, 即所有REST术语序列来源于该文,其中包含客户端-服务器交互的定义:

所有REST交互都是无状态的。也就是说,每个请求都包含了所有必要的信息,使连接器能够理解该请求,独立于之前可能存在的任何请求

这种限制实现了四个功能,其中第1个和第3个在这种情况下非常重要:

  • 第1个: 它消除了连接器在请求之间保留应用程序状态的需求,从而减少了物理资源的消耗并提高了可伸缩性;
  • 第3个: 它允许中介查看和理解单个请求,当服务被动态重新排列时可能是必要的;

现在让我们回到您的安全案例。每个请求都应包含所有必需的信息,授权/认证也不例外。如何做到这一点?字面意义上发送每个请求中所需的所有信息。

其中一个实现方法是基于哈希的消息认证码HMAC。实际上,这意味着将当前消息的哈希代码添加到每个请求中。哈希代码是通过密码散列函数秘密密码密钥相结合计算得出的。 密码散列函数可以是预定义的,也可以是按需编码REST概念的一部分(例如JavaScript)。秘密密码密钥应由服务器提供给客户端作为资源,并且客户端使用它来为每个请求计算哈希代码。

有很多HMAC实现的示例,但我想让您注意以下三个:

实际操作方式

如果客户端知道秘密密钥,则可以操作资源。否则,它将被临时重定向(状态码307临时重定向)以进行授权并获取秘密密钥,然后重定向回原始资源。在这种情况下,无需预先知道(即在某处硬编码)用于授权客户端的URL,并且可以随时间调整此模式。

希望这可以帮助您找到合适的解决方案!


24
MAC(消息认证码)旨在证明消息的真实性,防止篡改,与用户身份验证无关。 - yrk
1
增加了一个示例,演示如何处理用户/客户端身份验证,而不需要预先知道“_login URL_”。 - Akim
这里有另外两篇很好的文章,提供REST服务的无状态认证示例: http://blog.jdriven.com/2014/10/stateless-spring-security-part-2-stateless-authentication/ http://technicalrex.com/2015/02/20/stateless-authentication-with-spring-security-and-jwt/ - Vova Rozhkov

41

简而言之,每个请求的登录并非实现API安全性所必需,身份验证才是。

谈到登录时很难不谈及安全性问题。在某些身份验证方案中,没有传统的登录。

REST没有规定任何安全规则,但在实践中最常见的实现方式是OAuth 3向身份验证(正如您在问题中提到的)。至少不是每个API请求都需要登录。使用三向身份认证,您只需使用令牌。

  1. 用户批准API客户端,并授予权限以获取长寿命令牌,以形式化的方式发出请求。
  2. Api客户端通过使用长生命周期的令牌获得短期令牌。
  3. Api客户端带着短期令牌发送每个请求。

此方案使用户可以随时撤销访问权限。我看到的几乎所有公开可用的RESTful API都使用OAuth来实现此操作。

我认为您不应该将自己的问题(和问题)框定在登录方面,而应该考虑整体保护API的安全性。

有关一般情况下对REST API进行身份验证的更多信息,您可以查看以下资源:


是的,OAuth!非常直接的答案,我认为应该被接受的答案。 - Levite

26

REST哲学的一个重要部分是在设计API时尽可能利用HTTP协议的标准特性。 在身份验证方面应用这一哲学,客户端和服务器将在API中使用标准的HTTP身份验证功能。

登录界面非常适用于人类用户情况:访问登录界面,提供用户名/密码,设置cookie,客户端在所有未来请求中提供该cookie。不能期望使用Web浏览器的人每个HTTP请求都提供一个用户名和密码。

但对于REST API而言,登录界面和会话cookie并不是必需的,因为每个请求都可以包含凭据,而不影响人类用户;如果客户端任何时候不合作,则可以给出“未经授权”的401响应。 RFC 2617描述了HTTP中的身份验证支持。

TLS(HTTPS)也是一种选择,并且可以通过验证另一方的公钥来在每个请求中对客户端和服务器进行身份验证。此外,这还可为我们提供了安全通道的额外保障。当然,需要在通信之前进行密钥交换才能实现这一点。(请注意,这与使用TLS / Diffie-Hellman保护通道无关的用户识别/身份验证。即使您不使用公钥标识用户,使用TLS / Diffie-Hellman保护通道也总是一个好主意。)

举个例子:假设OAuth令牌是您的完整登录凭据。一旦客户端拥有OAuth令牌,则可以在每个请求中将其作为标准HTTP身份验证的用户ID提供。服务器可以在首次使用时验证令牌并缓存检查结果,并与每个请求一起更新具有生存时间的TTL。任何需要身份验证的请求如果没有提供则返回401


2
由于每个请求都可以包含凭据而不影响人类用户,因此发明了三方身份验证和OAuth。如果您在没有服务器撤销机制的情况下每次请求都提供凭据,则在没有SSL的情况下使用将是不安全的。 - Slavo
1
每当涉及到用户概念时,就需要从客户端传递一些东西到服务器来识别哪个用户。OAuth令牌可以作为“凭据”使用,而不是实际的用户名/密码组合。通过TLS保护通道肯定总是一件好事,但这几乎是次要的。即使您使用cookie,仍然会将某种类型的令牌与每个请求一起发送到服务器,只是使用cookie头而不是身份验证头。 - wberry
如果由于某种原因您没有使用TLS或OAuth,那么每次发送用户/密码是否真的比仅发送一次更糟糕?如果攻击者能够获取用户/密码,则攻击者很可能也能够获取会话cookie。 - wberry
1
Cookie和身份验证头作为凭据之间的区别在于,cookie始终与特定域相关联。这意味着当API接收到一个cookie时,它知道它来自哪里(之前由同一域编写)。对于头文件,您永远不知道,必须实现特定的检查。总的来说,我同意,它们都是凭据,但我认为传递凭据并不等同于登录。登录是打开门的主动操作。在3方身份验证的情况下,只有客户端的第一次批准才是登录。 - Slavo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接