ChatGPT解决这个技术问题 Extra ChatGPT

RESTful 身份验证是什么意思,它是如何工作的?我在 Google 上找不到很好的概述。我唯一的理解是您在 URL 中传递了会话密钥(remeberal),但这可能是非常错误的。

当我用谷歌搜索 Restful Authentication 时,我发现了十几个 RoR 插件。我假设那些不是你要找的。如果不是 RoR,那是什么语言?什么网络服务器?
如果您使用 HTTPS,它不会是可怕的错误。完整的 HTTP 请求连同 URL 将被加密。
@BharatKhatri:是的。我永远不会在用户可见的 URL 中传递敏感信息。出于实际目的,此信息更有可能泄漏。 HTTPS 对意外泄漏无能为力。
@jcoffland:真正的 RESTful 身份验证是什么意思?我很感兴趣,因为我刚刚从接受的答案中实现了第三种方式,但是我对此并不满意(我不喜欢 URL 中的附加参数)。
有些人使用 jwt.io/introduction 来解决这个问题。我现在对此进行研究以解决我的问题:stackoverflow.com/questions/36974163/… >>希望这会正常工作。

C
Community

如何在 RESTful 客户端-服务器架构中处理身份验证是一个有争议的问题。

通常,它可以在 SOA over HTTP 世界中通过以下方式实现:

基于 HTTPS 的 HTTP 基本身份验证;

Cookie 和会话管理;

HTTP 标头中的令牌(例如 OAuth 2.0 + JWT);

使用附加签名参数查询身份验证。

您必须适应,甚至更好地混合这些技术,以最好地匹配您的软件架构。

每个身份验证方案都有自己的优点和缺点,具体取决于您的安全策略和软件架构的目的。

基于 HTTPS 的 HTTP 基本身份验证

大多数 Web 服务都使用基于标准 HTTPS 协议的第一个解决方案。

GET /spec.html HTTP/1.1
Host: www.example.org
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

它很容易实现,默认在所有浏览器上都可用,但有一些已知的缺点,比如浏览器上显示的可怕的身份验证窗口会持续存在(这里没有类似 LogOut 的功能),一些服务器端额外的 CPU 消耗,以及用户名和密码(通过 HTTPS)传输到服务器的事实(在键盘输入期间让密码仅保留在客户端应该更安全,并作为安全哈希存储在服务器上) .

我们可以使用 Digest Authentication,但它也需要 HTTPS,因为它容易受到 MiMReplay 攻击,并且特定于 HTTP。

通过 Cookie 进行会话

老实说,在服务器上管理的会话并不是真正的无状态。

一种可能性是在 cookie 内容中维护所有数据。而且,按照设计,cookie 是在服务器端处理的(事实上,客户端甚至不会尝试解释这个 cookie 数据:它只是在每次连续请求时将其交给服务器)。但是这个 cookie 数据是应用程序状态数据,所以在一个纯粹的无状态世界中,应该由客户端而不是服务器来管理它。

GET /spec.html HTTP/1.1
Host: www.example.org
Cookie: theme=light; sessionToken=abc123

cookie 技术本身是 HTTP 链接的,所以它不是真正的 RESTful,它应该是独立于协议的,恕我直言。它容易受到 MiMReplay 攻击。

通过令牌 (OAuth2) 授予

另一种方法是在 HTTP 标头中放置一个令牌,以便对请求进行身份验证。例如,这就是 OAuth 2.0 所做的。请参阅the RFC 6749

 GET /resource/1 HTTP/1.1
 Host: example.com
 Authorization: Bearer mF_9.B5f-4.1JqM

简而言之,这与 cookie 非常相似并且存在相同的问题:不是无状态的,依赖于 HTTP 传输细节,并且受制于 a lot of security weaknesses - 包括 MiM 和 Replay - 所以只能通过 HTTPS 使用。通常,JWT 用作标记。

查询认证

查询身份验证包括通过 URI 上的一些附加参数对每个 RESTful 请求进行签名。请参阅this reference article

在这篇文章中是这样定义的:

所有 REST 查询都必须通过使用私有凭证作为签名令牌以小写字母顺序对查询参数进行签名来进行身份验证。签名应该发生在 URL 编码查询字符串之前。

这种技术可能与无状态架构更兼容,也可以通过轻量会话管理(使用内存会话而不是数据库持久性)来实现。

例如,下面是来自上述链接的通用 URI 示例:

GET /object?apiKey=Qwerty2010

应该这样传送:

GET /object?timestamp=1261496500&apiKey=Qwerty2010&signature=abcdef0123456789

被签名的字符串是 /object?apikey=Qwerty2010&timestamp=1261496500,签名是使用 API 密钥的私有组件对该字符串的 SHA256 哈希。

服务器端数据缓存始终可用。例如,在我们的框架中,我们在 SQL 级别缓存响应,而不是在 URI 级别。所以添加这个额外的参数不会破坏缓存机制。

有关我们的客户端-服务器 ORM/SOA/MVC 框架中基于 JSON 和 REST 的 RESTful 身份验证的一些详细信息,请参阅 this article。由于我们不仅允许通过 HTTP/1.1 进行通信,还允许通过命名管道或 GDI 消息(本地)进行通信,因此我们尝试实现真正的 RESTful 身份验证模式,而不依赖于 HTTP 特定性(如标头或 cookie)。

稍后注意:在 URI 中添加签名可能被视为不好的做法(例如,它将出现在 http 服务器日志中),因此必须减轻它,例如通过适当的 TTL 以避免重播。但是如果你的http日志被泄露,你肯定会遇到更大的安全问题。

在实践中,即将到来的MAC Tokens Authentication for OAuth 2.0可能是相对于“令牌授予”当前方案的巨大改进。但这仍然是一项正在进行的工作,并且与 HTTP 传输相关联。

结论

值得得出的结论是 REST 不仅基于 HTTP,即使在实践中,它也主要通过 HTTP 实现。 REST 可以使用其他通信层。因此,无论 Google 回答什么,RESTful 身份验证都不仅仅是 HTTP 身份验证的同义词。它甚至根本不应该使用 HTTP 机制,而是应该从通信层抽象出来。如果您使用 HTTP 通信,由于 Let's Encrypt initiative,没有理由不使用正确的 HTTPS,这是任何身份验证方案之外的必要条件。


如果您使用 Cookie 作为 HTTP Basic Auth 的更好替代品,您可以使用一种使身份验证过期和注销功能的方法进行真正的无状态身份验证。一个示例实现可以使用名为 Emulated-HTTP-Basic-Auth 的 cookie,该 cookie 具有与真正的 HTTP Basic Auth 相似的值,另外还设置了过期时间。然后可以通过删除该 cookie 来实现注销。我猜想 any 能够支持 HTTP Basic Auth 的客户端也可以支持以这种方式完成的 cookie 身份验证。
@MikkoRantalainen 但正如我所写,这个 cookie 仍将由服务器管理。它是某种无状态的,但不是“纯粹的”无状态。在所有情况下,您都需要专用于客户端登录/注销的 JavaScript 代码,即 perfectly possible e.g. with HTTP Digest Auth - 好主意,但在这里没有太大的好处,可以重新发明轮子。
我会声称服务器实现了用于配置标头的 UI 和逻辑,但标头本身是无状态的。为 API 设计的客户端可以跳过使用服务器帮助来配置标头,而只传递类似于 HTTP Basic Auth 的所需信息。我的观点是,常见的 UA(浏览器)对基本身份验证的实现如此糟糕,以至于无法使用。可以改用服务器为另一个标头 (Cookie) 中的相同内容提供的模拟。
只有当服务器通过发回 401 Unauthorized 响应来请求它时,才会出现用于 HTTP 授权的丑陋密码提示。如果您不喜欢它,只需发送 403 Forbidden 即可。错误页面可能包含登录方法或指向它的链接。但是,反对 cookie 和 http 身份验证(无论状态是服务器端还是客户端)的最大论据是它们容易受到跨站点请求伪造的影响。因此,最好的方法是自定义授权方案、自定义授权标头或自定义 GET 或 POST 参数。
M
Manikandan C

我怀疑那些热情地大喊“HTTP 身份验证”的人是否曾经尝试过使用 REST 制作基于浏览器的应用程序(而不是机器对机器的 Web 服务)(无意冒犯 - 我只是认为他们从未遇到过复杂情况) .

我发现在生成要在浏览器中查看的 HTML 页面的 RESTful 服务上使用 HTTP 身份验证的问题是:

用户通常会得到一个丑陋的浏览器制作的登录框,这对用户非常不友好。您不能添加密码检索、帮助框等。

注销或以不同的名称登录是一个问题 - 浏览器将不断向站点发送身份验证信息,直到您关闭窗口

超时很困难

here 是一篇非常有见地的文章,它逐点解决了这些问题,但这会导致 很多 特定于浏览器的 javascript 黑客攻击、变通方法的变通方法等。因此,它也不向前兼容,因此随着新浏览器的发布需要不断维护。我不认为这种简洁明了的设计,加上我觉得这是很多额外的工作和头痛,只是为了让我可以热情地向我的朋友展示我的 REST-badge。

我相信cookies是解决方案。但是等等,饼干是邪恶的,不是吗?不,他们不是,cookie 的经常使用方式是邪恶的。 cookie 本身只是一段客户端信息,就像浏览器在您浏览时跟踪的 HTTP 身份验证信息一样。并且这条客户端信息在每次请求时都会发送到服务器,就像 HTTP 身份验证信息一样。从概念上讲,唯一的区别是这块客户端状态的内容可以由服务器作为其响应的一部分来确定。

通过仅使用以下规则使会话成为 RESTful 资源:

会话将键映射到用户 ID(可能还有超时的最后操作时间戳)

如果会话存在,那么这意味着密钥是有效的。

登录意味着 POST 到 /sessions,一个新的密钥被设置为一个 cookie

注销意味着删除 /sessions/{key} (使用重载的 POST,请记住,我们是浏览器,HTML 5 还有很长的路要走)

通过在每次请求时将密钥作为 cookie 发送并检查会话是否存在且有效来完成身份验证

现在,与 HTTP 身份验证的唯一区别是身份验证密钥由服务器生成并发送给不断将其发回的客户端,而不是客户端根据输入的凭据计算它。

converter42 补充说,当使用 https(我们应该这样做)时,重要的是 cookie 将设置其安全标志,以便永远不会通过非安全连接发送身份验证信息。好点,我自己没见过。

我觉得这是一个可以正常工作的足够解决方案,但我必须承认,我不足以成为安全专家来识别该方案中的潜在漏洞——我所知道的是数百个非 RESTful Web 应用程序基本上使用相同的登录协议(PHP 中的 $_SESSION、Java EE 中的 HttpSession 等)。 cookie 标头内容仅用于寻址服务器端资源,就像接受语言可能用于访问翻译资源等一样。我觉得它是一样的,但也许其他人没有?你们觉得怎么样?


这是一个务实的答案,建议的解决方案有效。但是,在同一个句子中使用术语“RESTful”和“会话”是错误的(除非两者之间也有“不”;)。换句话说:任何使用会话的 Web 服务都不是 RESTful(根据定义)。不要误会我的意思——你仍然可以使用这个解决方案 (YMMV),但不能使用“RESTful”这个术语。我推荐 O'Reilly 的关于 REST 的书,它可读性很强,并且深入地解释了这个主题。
@skrebbel:纯 REST 解决方案会在每次请求资源时发送身份验证数据,这并不完美(HTTP Auth 这样做)。建议的解决方案适用于大多数用例并且更好,但它不是 RESTful。不需要战争,我也使用这个解决方案。我只是不声称它是 RESTful 的。 :)
哦,来吧,然后举个例子。还有什么办法,效果很好?我真的很想知道。 HTTP Auth 肯定不是,您无法在不关闭浏览器的情况下注销,并且如果没有大量特定于浏览器且不兼容未来的 JS,您将无法提供体面的登录 UX。我不太关心“纯 RESTful”与“几乎 RESTful”以及整个相关的宗教辩论,但如果你说有几种方法,你应该把它们拼出来。
使用真实世界用户代理(又名“浏览器”)的真正 RESTful 身份验证由包含 HTTP 身份验证值的 cookie 组成。这样,服务器可以提供用于输入登录名和密码的 UI,并且服务器可以强制注销(通过删除 cookie)。此外,当身份验证失败时,服务器必须使用临时重定向到登录屏幕,并在成功登录后使用临时重定向回以前的位置,而不是响应 401 要求登录。此外,服务器必须在登录用户的几乎每个页面中嵌入注销操作(POST 表单)。
只要很明显会话只存在于客户端,我认为在同一个句子中使用“restful”和“session”没有任何问题。我不确定为什么对这个概念如此重视。
2
2 revs, 2 users 94%

这里的好人已经对这个话题说得够多了。但这是我的 2 美分。

有2种交互方式:

人机对机器 (HTM) 机器对机器 (MTM)

机器是共同点,表示为 REST API,参与者/客户要么是人,要么是机器。

现在,在真正的 RESTful 架构中,无状态的概念意味着必须为每个请求提供所有相关的应用程序状态(即客户端状态)。相关是指 REST API 处理请求和提供适当响应所需的任何内容。

当我们在人机应用程序的上下文中考虑这一点时,正如 Skrebbel 指出的那样,“基于浏览器”,这意味着在浏览器中运行的(Web)应用程序将需要在每个请求中发送其状态和相关信息它适用于后端 REST API。

考虑一下:您有一个数据/信息平台公开的 REST API 资产。也许您有一个处理所有数据立方体的自助式 BI 平台。但是您希望您的(人类)客户通过 (1) Web 应用程序、(2) 移动应用程序和 (3) 一些第 3 方应用程序来访问它。最后,即使是 MTM 链也会导致 HTM - 对。所以人类用户仍然处于信息链的顶端。

在前两种情况下,您有一个人机交互的案例,信息实际上是由人类用户使用的。在最后一种情况下,您有一个使用 REST API 的机器程序。

身份验证的概念全面适用。您将如何设计它以便以统一、安全的方式访问您的 REST API?在我看来,有两种方法:

方式一:

没有登录,开始。每个请求都执行登录 客户端发送其识别参数 + 每个请求的请求特定参数 REST API 获取它们,转身,ping 用户存储(无论是什么)并确认身份验证 如果建立了身份验证,则为请求提供服务;否则,使用适当的 HTTP 状态代码拒绝对目录中所有 REST API 的每个请求重复上述操作

方式2:

客户端从身份验证请求开始 登录 REST API 将处理所有此类请求 它接受身份验证参数(API 密钥、uid/pwd 或您选择的任何内容)并根据用户存储(LDAP、AD 或 MySQL DB 等)验证身份验证。 ) 如果经过验证,则创建一个身份验证令牌并将其交还给客户端/调用者然后调用者将此身份验证令牌 + 请求特定参数与每个后续请求一起发送到其他业务 REST API,直到注销或租约到期

显然,在 Way-2 中,REST API 需要一种方法来识别和信任令牌是有效的。登录 API 执行了身份验证,因此您的目录中的其他 REST API 需要信任“代客密钥”。

当然,这意味着需要在 REST API 之间存储和共享身份验证密钥/令牌。这个共享的、受信任的令牌存储库可以是本地的/联合的,允许来自其他组织的 REST API 相互信任。

但我离题了。

关键是,需要维护和共享一个“状态”(关于客户端的身份验证状态),以便所有 REST API 可以创建一个信任圈。如果我们不这样做(即 Way-1),我们必须接受必须为任何/所有进入的请求执行身份验证行为。

执行身份验证是一个资源密集型过程。想象一下,针对您的用户存储对每个传入请求执行 SQL 查询以检查 uid/pwd 匹配。或者,加密和执行哈希匹配(AWS 风格)。在架构上,我怀疑每个 REST API 都需要使用通用的后端登录服务来执行此操作。因为,如果你不这样做,那么你就会到处乱扔身份验证代码。一个大乱子。

所以更多的层,更多的延迟。

现在,走 Way-1 并申请 HTM。您的(人类)用户是否真的关心您是否必须在每个请求中发送 uid/pwd/hash 或其他任何内容?不,只要您不通过每秒抛出身份验证/登录页面来打扰她。如果你这样做,祝你有客户好运。因此,您要做的是将登录信息存储在客户端的某处,在浏览器中,就在开始时,并在每次发出请求时发送。对于(人类)用户,她已经登录,并且“会话”可用。但实际上,她在每次请求时都经过身份验证。

与方式 2 相同。您的(人类)用户永远不会注意到。所以没有造成任何伤害。

如果我们将 Way-1 应用于 MTM 会怎样?在这种情况下,由于它是一台机器,我们可以通过要求它在每个请求中提交身份验证信息来让这个家伙感到厌烦。没人在乎!在 MTM 上执行 Way-2 不会引起任何特殊反应;它是一台该死的机器。它可以不在乎!

所以真的,问题是什么适合你的需要。无国籍是要付出代价的。付出代价并继续前进。如果你想成为一个纯粹主义者,那么也要为此付出代价,然后继续前进。

最后,哲学并不重要。真正重要的是信息发现、呈现和消费体验。如果人们喜欢你的 API,那么你就完成了你的工作。


先生,您对此进行了如此精美的解释,以至于我对手头的基本问题/问题有了清晰的认识。你就像佛一样!我可以补充一下,通过在传输层使用 HTTPS,我们甚至可以防止中间人攻击,这样就没有人劫持我的标识符密钥(如果选择 Way-1)
不总是一台机器进行身份验证吗?人类不会对密码胡说八道,这对于正确合理化安全性的用户来说是一种不幸的烦恼。对我来说,他们想让机器如何工作是开发人员的问题。
我读了你的回答;在您的解决方案中,对于用户点击在浏览器上发起的每个 Web 请求,都需要将“身份验证令牌”发送回用户点击调用的任何 API。然后怎样呢? API 对令牌执行检查。反对什么?针对维护该令牌是否有效的某种“令牌存储”。你不同意那个“令牌商店”然后成为“状态”的守护者吗?确实,无论您怎么看,某个地方的某个人都必须了解有关在用户活动中传递的“令牌”的一些信息。那就是状态信息所在的地方。
而“无状态”服务的真正含义是该特定服务器组件(CRUD API)不携带任何状态。他们不会从另一个用户中识别出一个用户,而是在一个事务中完整地完成用户请求。那就是无国籍。但是必须有人坐在某个地方并判断该用户是否有效。没有其他方法可以做到这一点;密钥或密码或其他任何东西。从用户端传递的任何东西都必须经过身份验证和授权。
您缺少混合方法 Way-3。客户端在 Way-2 中登录,但在 Way-1 中,不会根据任何服务器端状态检查凭据。无论如何,都会创建一个身份验证令牌并将其发送回客户端,如 Way-2 所示。稍后使用非对称加密检查此令牌的真实性,而无需查找任何客户端特定状态。
j
jcoffland

这是一个真正完整的 RESTful 身份验证解决方案:

在身份验证服务器上创建公钥/私钥对。将公钥分发给所有服务器。当客户端进行身份验证时: 3.1。发出一个令牌,其中包含以下内容: 到期时间 用户名(可选) 用户 IP(可选) 密码哈希(可选) 3.2。使用私钥加密令牌。 3.3.将加密的令牌发回给用户。当用户访问任何 API 时,他们还必须传入他们的身份验证令牌。服务器可以通过使用身份验证服务器的公钥对其进行解密来验证令牌是否有效。

这是无状态/RESTful 身份验证。

请注意,如果包含密码哈希,用户还将发送未加密的密码以及身份验证令牌。服务器可以通过比较哈希值来验证密码是否与用于创建身份验证令牌的密码匹配。需要使用 HTTPS 之类的安全连接。客户端的 Javascript 可以处理获取用户的密码并将其存储在客户端,无论是在内存中还是在 cookie 中,可能使用服务器的公钥加密。


如果有人获得了该身份验证令牌并假装是客户端调用 API 怎么办?
@Abidi,是的,这是个问题。您可能需要密码。密码的哈希值可以包含在身份验证令牌中。如果有人能够窃取令牌,它将很容易受到离线暴力攻击。如果选择了强密码,那将不是问题。请注意,如果您使用 https 令牌盗窃将要求攻击者首先获得对客户端计算机的访问权限。
非对称加密和解密比对称加密慢一个数量级(计算量更大)。让服务器在每次调用时使用公钥解密令牌将是一个巨大的性能瓶颈。
@jcoffland 你真的在这里推广了你的答案(反复:-)但我不禁评论在每次通话中使用非对称加密的性能问题(计算强度)。我只是看不到具有任何扩展能力的解决方案。查找 HTTPS 和 SPDY 协议。它竭尽全力保持连接打开(HTTP keep-alives,即状态),并通过同一连接批量提供多个资源(更多状态),当然 SSL 本身仅使用非对称加密来交换对称密钥(也说明)。
...因为对称加密比非对称加密快一个数量级。 HTTPS 最慢、最堵塞管道的方面是最初的握手,涉及使用公钥/私钥来加密消息。如果 HTTPS 没有为所有随后的通信切换到共享秘密对称加密,那么实际的、实际的现实世界的性能将是不可接受的,并且该解决方案永远无法扩展,至少在没有不可接受的资源成本的情况下是这样。
a
arg20

老实说,我在这里看到了很好的答案,但让我有点困扰的是,当有人将整个无状态概念推向极端时,它会变得教条主义。它让我想起了那些只想拥抱纯 OO 的 Smalltalk 老粉丝,如果某些东西不是对象,那么你做错了。让我休息一下。

RESTful 方法应该让您的生活更轻松并减少会话的开销和成本,尝试遵循它,因为它是明智的做法,但是当您遵循一个学科(任何学科/指南)到它的极端时不再提供预期的好处,那么你做错了。当今一些最好的语言同时具有函数式编程和面向对象。

如果您解决问题的最简单方法是将身份验证密钥存储在 cookie 中并将其发送到 HTTP 标头,那么就这样做,只是不要滥用它。请记住,当会话变得又重又大时,它们是不好的,如果您的所有会话都包含一个包含密钥的短字符串,那么有什么大不了的?

我愿意接受评论中的更正,但我只是不明白(到目前为止)让我们的生活变得悲惨只是避免在我们的服务器中保留一个大的哈希字典。


人们并没有试图禁止您使用会话。你可以自由地去做。但如果你这样做,它就不是 REST。
@AndréCaldas 它不是 REST,就像在语言中具有函数或原始类型不是 oop 一样。我并不是说开会是可取的。我只是在一定程度上就不再为某人提供好处的情况下遵循一套做法发表我的看法。 (顺便说一句,请注意我没有反对您的言论,但是,我不会说这不是 REST,我会说这不是纯 REST)。
那么如果它不是 RESTful,我们怎么称呼它呢?当然,如果请求包含会话 ID,那么这与包含用户 ID 的请求一样无状态吗?为什么用户 ID 是无状态的,而会话 ID 是有状态的?
Cookie 容易受到跨站点请求伪造的影响,因此它们更容易出现安全漏洞。最好使用浏览器不会自动发送的东西,例如自定义标头或自定义授权方案。
事实上,试图成为无状态并不是教条主义,而是关于 SOA 本身的一种常见概念。服务应该始终受益于解耦和无状态:在实践中,它简化了扩展、可用性和可维护性。当然,应该尽可能的多,最终还是需要一些“编排服务”来将那些无状态的服务管理成有状态的实用方法。
J
Jonathan DS

首先,RESTful Web 服务是无状态的(或者换句话说,无会话的)。因此,RESTful 服务没有也不应该包含会话或 cookie 的概念。在 RESTful 服务中进行身份验证或授权的方法是使用 RFC 2616 HTTP 规范中定义的 HTTP 授权标头。每个请求都应包含 HTTP Authorization 标头,并且应通过 HTTPs (SSL) 连接发送请求。这是在 HTTP RESTful Web 服务中进行身份验证和验证请求授权的正确方法。我在 Cisco Systems 为 Cisco PRIME Performance Manager 应用程序实施了 RESTful Web 服务。作为该 Web 服务的一部分,我还实现了身份验证/授权。


HTTP 身份验证仍然需要服务器跟踪用户 ID 和密码。这不是完全无国籍的。
从某种意义上说,它是无状态的,每个请求本身都是有效的,而不需要先前请求的任何要求。如何在服务器上实现是另一回事,如果身份验证很昂贵,您可以进行一些缓存并在缓存未命中时重新进行身份验证。很少有服务器是完全无状态的,其中输出纯粹是输入的函数。它通常是对某个状态的查询或更新。
不对。在这种情况下,您的所有请求都需要来自先前事务的状态,即用户注册。我不明白为什么人们一直试图说存储在服务器上的用户名和密码不是服务器端状态。看我的回答。
@jcoffland 此外,您的解决方案在很大程度上依赖于 API 服务器解密签名令牌的能力。我认为这种方法不仅过于具体,而且有点过于复杂以至于不能被认为是 R. Fielding 解决 RESTful 身份验证问题的方法。
@jcoffland 您是否了解非对称加密的计算密集程度(因此资源密集且极其缓慢)的程度?您正在谈论的方案将在每个请求上使用非对称加密。 HTTPS 最慢的方面,除此之外,是最初的握手,它涉及创建公钥/私钥以不对称地加密共享密钥,该密钥随后用于对称地加密所有随后的通信。
J
Justin Sheehy

它当然不是关于“会话密钥”,因为它通常用于指代在 REST 的所有约束内执行的无会话身份验证。每个请求都是自描述的,携带足够的信息来自行授权请求,而无需任何服务器端应用程序状态。

解决此问题的最简单方法是从 RFC 2617 中的 HTTP 内置身份验证机制开始。


HTTP 身份验证要求服务器存储用户名和密码。这是服务器端状态,因此不是严格意义上的 REST。看我的回答。
@jcoffland:这根本不是真的,在这两个方面。第一个 HTTP Auth 不需要服务器存储密码。而是存储密码的哈希值(推荐使用 8 轮以上的 bcrypt)。其次,服务器没有任何状态,因为每次请求都会发送授权标头。而且,如果您将存储的密码哈希视为状态,它们不会比存储的公钥更多的状态。
@Boris B.,是的,我知道密码存储为哈希。散列密码仍然是客户端特定的状态。如我的解决方案中所述,存储公钥的区别在于只有一个公钥,即身份验证服务器的公钥。这与为每个用户存储密码哈希非常不同。如果服务器为每个用户存储一个密码,那么无论你如何打扮它,它都会存储每个用户的状态,而不是 100% REST。
我不认为在服务器上存储用户散列密码应该被视为服务器端状态。用户是资源,包含名称、地址或哈希密码等信息。
C
Community

2019 年 2 月 16 日更新

下面前面提到的方法本质上是 OAuth2.0 的“资源所有者密码凭据”授予类型。这是启动和运行的简单方法。但是,通过这种方法,组织中的每个应用程序最终都将拥有自己的身份验证和授权机制。推荐的方法是“授权码”授权类型。此外,在我之前的回答中,我推荐使用浏览器 localStorage 来存储身份验证令牌。但是,我开始相信 cookie 是实现此目的的正确选择。我在this StackOverflow answer中详细说明了我的原因、授权码授予类型的实现方式、安全注意事项等。

我认为以下方法可用于 REST 服务身份验证:

创建一个登录 RESTful API 以接受用户名和密码进行身份验证。在传输过程中使用 HTTP POST 方法来防止缓存和 SSL 以确保安全 在成功验证后,API 返回两个 JWT - 一个访问令牌(有效期较短,例如 30 分钟)和一个刷新令牌(有效期较长,例如 24 小时) 客户端(一个基于 Web 的 UI)将 JWT 存储在本地存储中,并且在每个后续 API 调用中都会在“Authorization: Bearer #access token”标头中传递访问令牌 API 通过验证签名和到期日期来检查令牌的有效性。如果令牌有效,请检查用户(它将 JWT 中的“子”声明解释为用户名)是否可以通过缓存查找访问 API。如果用户被授权访问 API,则执行业务逻辑 如果令牌过期,API 返回 HTTP 响应代码 400 客户端在收到 400/401 时,使用“授权:承载#”中的刷新令牌调用另一个 REST API刷新令牌”标头以获取新的访问令牌。在收到带有刷新令牌的调用时,通过检查签名和到期日期来检查刷新令牌是否有效。如果刷新令牌有效,则从数据库中刷新用户的访问权限缓存,并返回新的访问令牌和刷新令牌。如果刷新令牌无效,则返回 HTTP 响应码 400 如果返回新的访问令牌和刷新令牌,则转到步骤 2。如果返回 HTTP 响应码 400,则客户端假定刷新令牌已过期并询问用户名和来自用户的密码对于注销,清除本地存储

使用这种方法,我们每 30 分钟执行一次使用特定于用户的访问权限详细信息加载缓存的昂贵操作。因此,如果撤销访问权限或授予新访问权限,则需要 30 分钟才能反映或注销后再登录。


那么,您会将其用于带有 Angular 静态网站的 api 吗?那么移动应用程序呢?
J
Ji Han

@skrebel ( http://www.berenddeboer.net/rest/authentication.html ) 提到的“非常有见地”的文章讨论了一种复杂但非常糟糕的身份验证方法。

您可以尝试在没有任何登录凭据的情况下访问该页面(该页面应该只对经过身份验证的用户可见)http://www.berenddeboer.net/rest/site/authenticated.html

(对不起,我无法评论答案。)

我会说 REST 和身份验证根本不混合。 REST 表示无状态,但“经过身份验证”是一种状态。您不能将它们都放在同一层。如果您是 RESTful 倡导者并且对状态不满意,那么您必须使用 HTTPS(即将安全问题留给另一层)。


Stripe.com 会对您对 REST 和身份验证不混合的评论表示不同的看法。
无状态仅指服务器,而不是客户端。客户端可以记住会话的所有状态并发送与每个请求相关的内容。
终于有人讲了一些道理,但是使用公钥加密可以进行无状态身份验证。看我的回答。
服务器没有“已验证”状态。它通过超媒体接收信息,并且必须与它一起返回所请求的内容。不多不少,不多不少。如果资源受到保护并需要身份验证和授权,则提供的超媒体必须包含该信息。我不知道在返回资源之前对用户进行身份验证意味着服务器正在跟踪状态的概念来自哪里。提供用户名和密码可以很好地被认为只是提供更多过滤参数。
“我会说 REST 和身份验证根本不能混合。”听起来有点常识。除了与认证不兼容的系统(“认证”本身当然是一个状态)之外,它的用处有限。我觉得我们都在争论实用性和纯粹教条主义的交叉点,坦率地说实用性应该会获胜。 REST 的许多方面都非常有益,而且不会为了避免与身份验证相关的状态而扭曲,不是吗?
B
Bjorn

我认为 RESTful 身份验证涉及将身份验证令牌作为请求中的参数传递。示例是 api 对 apikeys 的使用。我不相信使用 cookie 或 http auth 符合条件。


由于 CSRF 漏洞,应避免使用 Cookie 和 HTTP Auth。
@DobesVandermeer 如果你能帮忙,你能看看我的问题吗? stackoverflow.com/questions/60111743/…
H
HelpNeeder

这就是这样做的方法:Using OAuth 2.0 for Login

您可以使用除 Google 之外的其他身份验证方法,只要它支持 OAuth。


没有 HTTPS,OAuth2 是不安全的,也不是无状态的。
没有 HTTPS,没有什么是安全的。
@Craig 并且 HTTPS 也可能不安全,如果证书链被破坏,这可能会带来更大的好处 - en.wikipedia.org/wiki/Bullrun_(decryption_program) ;)
@ArnaudBouchez 请澄清证书链损坏如何带来更大的利益?我不明白你要去哪里。 ;)
@Craig 请点击链接,尽情享受吧!在我的评论中,这种“更大的好处”方法显然是玩世不恭的:类似 Bullrun 的系统是为了“我们自己的利益”而由我们敬爱和信任的政府来设计的。
D
DonB.

使用公钥基础设施,其中密钥的注册涉及正确绑定,可确保公钥以确保不可否认性的方式绑定到分配给它的个人

请参阅http://en.wikipedia.org/wiki/Public_key_infrastructure。如果您遵循正确的 PKI 标准,则可以识别并锁定不当使用被盗密钥的个人或代理。如果代理需要使用证书,则绑定会变得非常紧密。一个聪明敏捷的小偷可以逃走,但他们会留下更多的面包屑。


P
Pang

适用于保护任何 Web 应用程序的提示

如果您想保护您的应用程序,那么您绝对应该从使用 HTTPS 而不是 HTTP 开始,这可以确保在您和用户之间创建安全通道,防止嗅探来回发送给用户的数据并有助于保留数据交换机密。

您可以使用 JWT(JSON Web Tokens)来保护 RESTful API,与服务器端会话相比,这有很多好处,好处主要是:

1- 更具可扩展性,因为您的 API 服务器不必为每个用户维护会话(当您有很多会话时,这可能是一个很大的负担)

2- JWT 是自包含的,并且具有定义用户角色的声明,例如,他可以访问和在日期和到期日发布的内容(在此之后 JWT 将无效)

3-更容易跨负载均衡器处理,如果您有多个 API 服务器,因为您不必共享会话数据,也不必配置服务器以将会话路由到同一服务器,只要带有 JWT 的请求命中任何服务器,就可以对其进行身份验证&授权

4-减少对数据库的压力,并且您不必为每个请求不断存储和检索会话 ID 和数据

5-如果您使用强密钥签署 JWT,则 JWT 不会被篡改,因此您可以信任随请求发送的 JWT 中的声明,而无需检查用户会话以及他是否被授权,您只需检查 JWT 就可以知道该用户可以做什么和做什么。

许多库提供了简单的方法来创建 &在大多数编程语言中验证 JWT,例如:在 node.js 中,最流行的一种是 jsonwebtoken

由于 REST API 通常旨在保持服务器无状态,因此 JWT 与该概念更兼容,因为每个请求都使用自包含的授权令牌 (JWT) 发送,与使服务器是有状态的,因此它可以记住用户及其角色,但是,会话也被广泛使用并具有其优点,您可以根据需要进行搜索。

需要注意的一件重要事情是,您必须使用 HTTPS 将 JWT 安全地交付给客户端并将其保存在安全的地方(例如本地存储中)。

您可以了解有关 JWT 的更多信息from this link


佚名

根据我的理解来回答这个问题...

使用 REST 的身份验证系统,因此您无需实际跟踪或管理系统中的用户。这是通过使用 HTTP 方法 POST、GET、PUT、DELETE 完成的。我们采用这 4 种方法,并在数据库交互方面将它们视为 CREATE、READ、UPDATE、DELETE(但在 Web 上我们使用 POST 和 GET,因为这是锚标签当前支持的)。因此,将 POST 和 GET 视为我们的 CREATE/READ/UPDATE/DELETE (CRUD),然后我们可以在我们的 Web 应用程序中设计路由,从而能够推断出我们正在实现的 CRUD 操作。

例如,在 Ruby on Rails 应用程序中,我们可以构建我们的 Web 应用程序,这样如果登录的用户访问 http://store.com/account/logout,那么该页面的 GET 可以被视为试图注销的用户。在我们的 Rails 控制器中,我们将构建一个操作,将用户注销并将其发送回主页。

登录页面上的 GET 将产生一个表单。登录页面上的 POST 将被视为登录尝试,并获取 POST 数据并使用它进行登录。

对我来说,这是一种使用映射到其数据库含义的 HTTP 方法然后构建身份验证系统的做法,并牢记您不需要传递任何会话 ID 或跟踪会话。

我还在学习——如果您发现我所说的任何错误,请纠正我,如果您了解更多,请在此处发布。谢谢。