注销:GET 还是 POST?

Posted

技术标签:

【中文标题】注销:GET 还是 POST?【英文标题】:Logout: GET or POST? 【发布时间】:2011-04-01 01:56:27 【问题描述】:

这个问题与一般何时使用 GET 或 POST 无关;它是关于处理退出 Web 应用程序的推荐使用哪个。我已经找到了大量关于一般意义上的 GET 和 POST 之间差异的信息,但我没有找到针对这种特定场景的明确答案。

作为一个实用主义者,我倾向于使用 GET,因为实现它比 POST 简单得多;只需删除一个简单的链接,您就完成了。我能想到的绝大多数网站似乎都是这种情况,至少在我的脑海中是这样。甚至 Stack Overflow 也使用 GET 处理注销。

让我犹豫的是(尽管是旧的)论点,即一些网络加速器/代理通过访问并检索他们在页面中找到的每个链接来预缓存页面,因此用户在点击它们时会得到更快的响应。我不确定这是否仍然适用,但如果是这种情况,那么理论上具有这些加速器之一的用户一旦登录就会被踢出应用程序,因为她的加速器会找到并检索注销链接,即使她从未点击过。

到目前为止,我所阅读的所有内容都表明 POST 应该用于“破坏性操作”,而不会改变应用程序内部状态的操作(例如查询等)应该使用 GET 处理。基于此,这里真正的问题是:

退出应用程序是否被视为破坏性操作/是否会改变应用程序的内部状态?

【问题讨论】:

好吧,假设您是第一次访问该站点,并且没有注销链接,那么您在登录时就会被注销。再次登录后就好了,因为注销 url 已经被缓存了。但是可以假设任何像样的加速器都能够过滤掉大多数注销 url。 HyperCas,加速器过滤出注销 URL 是我正在考虑的一个理论,也是我决定发布这个问题的原因之一。我有点不情愿只相信加速器逻辑,有一天有一个使用糟糕加速器的用户抱怨她无法登录。你知道他们是否遵循标准,或者是否存在这样的标准? 任何自动提交表单的加速器(例如)都是恶意软件 IMO...认为加速器会自动提交表单是完全不合逻辑的。想象一下你访问谷歌。它如何提交搜索表单?没有人可以解释恶意软件,因为它太不可预测并且不遵守规则。 @AlexW - 我想你误解了我的问题。我提出的加速器方案是在使用 GET 而不是 POST 时显示一个可能的问题,因此不会有要发布的表单,只有简单的链接,加速器不会出现问题。 我意识到我已经为时已晚,但亚历克斯,这不是丹尼尔要问的。他的意思是,如果用户单击注销链接并且加速器返回缓存的注销页面而不点击应用程序,那么用户将保持登录状态。与恶意软件无关,尽管 FYI 检查用户代理字符串不会修复无论如何。 【参考方案1】:

使用POST

在 2010 年,使用 GET 可能是一个可以接受的答案。但是今天(2013 年),浏览器会预取他们“认为”你接下来会访问的页面。

这里是 *** 开发者之一在 twitter 上谈论这个问题:

感谢我的银行注销 GET 请求,感谢 Chrome 团队提供方便的 URL 预取。- Nick Craver (@Nick_Craver) January 29, 2013

有趣的事实:*** 曾经通过 GET 处理注销,但现在不是了。

【讨论】:

感谢您的更新,戴夫。我什至没有注意到 SO 将他们的日志切换为 POST,老实说,我不知道 Chrome 内置了预取功能。最后,你引用的 twit 可能永远无法为我在我的问题中描述的问题提供更好的例子我的问题并证实了我的怀疑。我正在对您的答案进行投票并使其成为公认的答案。 在我的浏览器中,*** 注销看起来像 注销 这是一个 GET,而不是一个 POST跨度> @Mark0978,点击链接。 有趣。这可能是我最不喜欢的功能之一,注销然后询问我是否确定。猜猜它可以防止预取让您注销,但亚马逊、Ebay 和 Gmail 都使用 GET 进行注销,而在用户被告知注销和实际注销事件之间没有那个技巧页面。我想在页面之间会导致很多人错误地认为他们已注销。 SO 上的问题很小,不涉及金钱,而且 99% 的内容都是公开的。 @Red 根据 HTTP/1.1 标准,这是服务器的问题,而不是浏览器的问题。 GET 预计不会在服务器端产生副作用。该标准甚至说“用户没有要求副作用,因此不能对它们负责”。【参考方案2】:

在 REST 中不应该有会话,因此没有什么可破坏的。 REST 客户端对每个请求进行身份验证。登录或退出,这只是一种错觉。

您真正要问的是浏览器是否应该继续在每个请求上发送身份验证信息。

可以说,如果您的应用程序确实产生了登录的错觉,那么您应该能够使用 javascript“注销”。无需往返。


Fielding 论文 - Section 5.1.3

从客户端到服务器的每个请求 必须包含所有信息 理解请求所必需的, 并且不能利用任何 存储在服务器上的上下文。会议 因此状态完全保持在 客户

【讨论】:

我实际上并没有意识到这一点。然后我猜我的应用程序根本不会很 RESTful,因为我使用 ASP.NET MVC 和 FormsAuthentication 并且它依赖于会话...... 但实际上登录信息保存在一个用httponly属性标记的cookie中,以防止一些xss风险,这意味着它只能从服务器重置(没有手动清除cookie) “手动”,因为用户进入浏览器设置并选择“清除 cookie”选项。 “注销”网站几乎不是一种可接受的方式。 @Remus Ahhh,杰出的网络浏览器如何让编写网络应用程序变得如此痛苦。 @DarrelMiller 是的,但是不撤销服务器端的 JWT 是一个安全漏洞。即使令牌没有存储在服务器上,也应该在用户注销/更改密码/更改角色/退出/等时将它们列入黑名单,以防止滥用(至少在它们过期之前)。【参考方案3】:

GET 在这里可能被滥用的一种方式是,一个人(也许是竞争对手:)在互联网上的任何地方放置了一个带有 src="<your logout link>" 的图像标签,如果您网站的用户偶然发现该页面,他会在不知不觉中登出。

【讨论】:

不,这是不对的。仅当发送正确的 cookie 数据时,注销链接才有效,它不会来自另一个域。即使会话 id 存储在 url 中,这也不会起作用,因为每个会话都会更改。 哇,我从来没想过!所以,不使用 GET 的另一个原因,以及我不明白为什么每个人都这样做的另一个原因。该死的,现在我很想在我的帖子中包含一个***.com/users/logout“图像”,看看会发生什么:-D src= 是一个简单的浏览器请求,它不是来自服务器端,而是来自客户端。它携带所有 cookie 并来自用户 IP。这就是广告跟踪像素起作用的原因。确定此类漏洞的唯一方法是检查引荐来源网址。 SuperLogout.com 正是这样做的(在隐藏图像中加载 /logout URL),并且它可以工作。 re:SuperLogout ...我不知道我为什么点击它。【参考方案4】:

您好,在我看来,当您登录时检查用户名/密码,如果匹配,则创建登录令牌。

创建令牌 => 方法 POST

当您注销时,您会破坏令牌,所以对我来说最合乎逻辑的方法应该是删除

DELETE 令牌 => 方法 DELETE

【讨论】:

有趣的角度。 我在我的 Spring Boot REST 应用程序中使用该方法。 是的,但 DELETE 的 URL 应该标识要删除的资源,因此它不能与 POST 中的相同。 @RonInbar 同意,如果遵循 REST 100%,那么创建会话的 POST 应该转到 [主机名]/[用户名]/会话,如下文所述 VinayC。 DELETE 应该类似于 [host name]/[user name]/session/[session_id]。然而,使用 DELETE 处理会话资源的删除,即使从 cookie 而不是 URL 获取资源,也比使用另一个动词删除资源更好(同样,如果尝试遵循 REST 约定)。【参考方案5】:

正确地说,GET/POST(或其他动词)是对某些资源(由 URL 寻址)的操作 - 所以它通常与资源的状态有关,而不是与应用程序状态本身有关。因此,本着真正的精神,您应该有一个诸如 [host name]\[user name]\session 之类的 URL,然后“删除”将是注销操作的正确动词。

使用[host name]\bla bla\logout 作为 URL 并不是真正的 REST 完整方式 (IMO),那么为什么要争论正确使用 GET/POST 呢?

当然,我也在我的应用程序中使用 GET 来注销 url :-)

【讨论】:

在这种情况下,我认为在 URL 中包含 [用户名] 部分似乎没有必要,因为用户总是从(即删除)他们自己的会话中注销;从来没有其他用户的:-) 不是真的——我们说会话是一种资源,我们想删除它。因此,为了统一处理任何会话,您需要将用户名作为 URL 的一部分。您的论点就像说对 [照片库]\图片发出 PUT 操作意味着您正在添加到您的照片(可在 [照片库]\[用户名]\图片中找到)。必须明确处理不同的资源,其中不能有任何隐含性。该站点可能允许其他用户将图片添加到您的画廊 - 这将是访问控制的一部分,就像您可以拥有一个可以杀死任何人的会话的超级用户一样。 从哲学上讲,您可以将会话和照片称为“资源”,但实际上我不会对它们一视同仁。会话本质上总是仅限于当前用户(因此称为会话),并且至少在 ASP.NET 中,无法访问其他用户的会话。甚至应用程序开发人员也无法直接枚举所有活动会话,或单独终止会话的方法。您可以重新启动应用程序以终止所有会话(InProc),但我不会调用该访问控制。除了 URL,问题仍然存在:GET 还是 POST? 资源,因此它的地址 (URL) 是 REST 的重要组成部分。因此,如果您像我所说的那样选择 URL,则 DELETE 将成为正确的词 - 而不是 GET 或 POST。此外,即使您将自己限制在 ASP.NET 中,您也始终可以使用自定义状态提供程序,它可以让您枚举会话并在需要时终止其他会话。对于开箱即用的进程内会话,global.asax 中的一些摆弄应该为您提供功能。这真的是一个是否需要这种功能的问题。对于不常见的需求,人们倾向于重新启动网站以将人们赶出网站。 这对我来说最有意义。给 web api 一个会话路由并在其上调用 DELETE。是 ../session 或 ../session/current。谢谢@VinayC【参考方案6】:

注销对应用程序本身没有任何影响。它改变了用户与应用程序相关的状态。在这种情况下,您的问题似乎更多地基于用户应如何启动命令以开始此操作。由于这不是“破坏性操作”,因此请确保会话被放弃或销毁,但您的应用程序或您的数据均未更改,因此允许两种方法启动注销过程并非不可行。该帖子应由任何用户发起的操作(例如 - 用户单击“注销”)使用,而 get 可以保留用于应用程序发起的注销(例如 - 检测到潜在用户入侵的异常会通过注销 GET 强行重定向到登录页面)。

【讨论】:

有趣;我从来没有这样想过。 +1。 这可能取决于应用程序(某种“级联删除”行为),但你是对的。 @JoelEtherton 谢谢乔尔,我正在阅读答案,想知道什么时候能找到正确的答案。 :) 这很令人困惑,因为注销会更改状态。 POST 是改变状态的动词。 GET 用于获取无状态数据。这很令人困惑,因为我们希望 POST 请求具有有效负载。如下所述,DELETE 在会话对象上最正确。 @MichaelCole:我同意 POST 和 GET 之间的困难表示。不过,我不同意 DELETE 动词的用法。 DELETE 用于处理资源,会话不是这个意义上的资源。考虑一下,如果你可以删除它,那么你也应该可以 PUT。【参考方案7】:

预缓存的场景是一个有趣的场景。但我猜如果很多网站公司都不担心这一点,那么也许你也不应该担心。

或者链接可以用javascript实现?

编辑:据我了解,从技术上讲,GET 应该用于只读请求,不会更改应用程序状态。 POST 应该用于更改状态的写入/编辑请求。然而,对于一些状态变化的请求,其他应用程序问题可能更喜欢 GET 而不是 POST,我认为这没有任何问题。

【讨论】:

谢谢。 DB 状态不会改变,但 session 状态会改变。我看到的唯一问题是我在问题中提到的关于用户被踢出的问题。非破坏性但相当烦人。我通常也会遵循“如果大人物这样做,那一定没问题”的口头禅。我只是想知道其他人对此有何看法。【参考方案8】:

如果您让您的 Web 应用程序通过注销脚本放弃会话,您通常也不需要。通常有一个会话变量对于您要放弃的会话是唯一的。

【讨论】:

您能详细说明“注销脚本”吗?我不确定您是否指的是设置 cookie 过期时间(这并不能消除让用户手动注销的需要。) 注销脚本将结束调用它的用户(实际上是浏览器)的会话。在 ASP.net 中,会话是可以放弃的服务器端对象。 php 也有类似的系统。因为该浏览器调用结束会话的脚本,它已经知道要结束哪一个,从而无需 POST 或 GET 变量。 是的,我现在知道了。我已经准备好脚本,特别是 FormsAuthentication.SignOut(),但我的问题是关于 如何 调用脚本,如在 GET 或 POST 中。 哦,你有表单中的 url 吗?如果您不向其传递任何信息,则无关紧要。可能发生的最糟糕的事情是有人手动打开脚本,将自己注销。如果没有必要,我什至不会将其设置为表单字段,脚本的链接也可以。如果您确实向脚本发送信息,我可能会进行 POST,以免向用户显示任何信息(除非他们查看页面源代码),如果他们刷新,他们会从浏览器收到警告(页面已过期),这可能是可取的。【参考方案9】:

我不明白注销(取消提升用户权限)是一种破坏性行为。那是因为“注销”操作应该只对已经登录的用户可用,否则它将被过时。

您的浏览器 cookie 中包含的随机生成的字符串都代表您的用户会话。有很多方法可以销毁它,因此有效地注销只是为您的访问者提供的服务。

【讨论】:

wget 在蜘蛛模式下,在私有 wiki 上使用正确的会话 cookie 是我实际上必须做的一次。当然,最早抓取的网址之一是/logout 尝试前往SuperLogout.com 看看对/logout 页面的GET 请求的破坏性究竟有多大。例如,您必须再次登录 Gmail,再次登录聊天,在您滚动的任何环聊对话中找到自己的位置等 - 这仅适用于 Google.com。 @DanDascalescu 您应该警告人们访问此页面将立即注销他们并且没有确认按钮! 非常生气的表情

以上是关于注销:GET 还是 POST?的主要内容,如果未能解决你的问题,请参考以下文章

<a> 标签是 post 方法还是 get 方法?

GET 还是 POST?

检查请求是 GET 还是 POST [重复]

检查请求是 GET 还是 POST

为啥 Laravel 默认通过 POST 注销(而不是 GET)? [复制]

基本身份验证请求应该是 POST 还是 GET?