注销:GET还是POST?


434

这个问题与一般何时使用GET或POST无关。推荐使用哪种方法来处理从Web应用程序注销。在一般意义上,我已经找到了很多有关GET和POST之间差异的信息,但是对于这种特殊情况,我没有找到确切的答案。

作为实用主义者,我倾向于使用GET,因为实现它比POST更简单。只需删除一个简单的链接就可以了。我可以想到的绝大多数网站都是这种情况,至少从我的脑海中可见。甚至Stack Overflow也可以使用GET进行注销。

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

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

从应用程序注销是否被认为具有破坏性,是否会改变应用程序的内部状态?


好吧,假设您是第一次访问该站点,并且不存在注销链接,那么登录时您将被注销。再次登录后就可以了,因为注销URL已被缓存。但是可以假定任何一款不错的加速器都可以过滤掉大多数注销URL。
HyperCas

2
HyperCas,加速器过滤掉注销URL是我正在考虑的一种理论,也是我决定发布该问题的原因之一。我感到有点不愿意仅仅信任加速器逻辑,有一天,一个with脚的加速器用户抱怨她无法登录。您知道他们是否遵循某个标准,或者该标准是否存在?
Daniel Liuzzi 2010年

任何自动提交表单的加速器(例如)都是恶意软件IMO ...认为加速器会自动提交表单是完全不合逻辑的。假设您访问了Google。如何提交搜索表格?没有人能够解释恶意软件,因为它太难以预测且不遵守规则。
亚历克斯(Alex)2010年

3
@AlexW-我想您误解了我的问题。我提出的加速器方案是为了显示使用GET而不是POST时可能出现的问题,因此将没有形式发布,只有简单的链接加速器将不会出现问题。
Daniel Liuzzi 2010年

1
我意识到我为此还为时已晚,但是Alex,这不是Daniel所要问的。他说的是,如果用户单击注销链接,并且加速器在未点击应用程序的情况下返回了缓存的注销页面,则该用户将保持登录状态。与恶意软件无关,尽管FYI检查User-Agent字符串无法解决无论如何。
罗伯·格兰特

Answers:


475

使用POST

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

这是StackOverflow开发人员之一在Twitter上谈论此问题:

我要感谢我的银行注销GET请求,并感谢Chrome小组方便的URL预取。-Nick Craver(@Nick_Craver2013年1月29日

有趣的事实:StackOverflow曾经用于通过GET处理注销,但现在不再。


2
谢谢您的更新,戴夫。我什至没有注意到SO会将他们的登录信息切换为POST,老实说我不知道​​Chrome内置了预取功能。最后,您引用的花招永远无法为我在我的文章中描述的问题提供更好的示例我的问题,并证实了我的怀疑。我正在投票给您答案,并使其成为可接受的答案。
Daniel Liuzzi

4
在我的浏览器中,Stackoverflow注销看起来像<li> <a href="https://stackoverflow.com/users/logout">注销</a> </ li>,这是GET而不是POST
boatcoder 2013年

9
@ Mark0978,单击链接。
大卫·默多克

2
有趣。那可能是我最不喜欢的功能之一,然后注销,然后问我是否确定。猜测它可以阻止预取退出,但是Amazon,Ebay和Gmail都使用GET进行注销,而在告知用户注销和实际注销事件之间没有技巧页面。我可以想象在这两个页面之间会导致许多人错误地认为自己已注销。SO的问题是最小的,不涉及金钱,而且所有内容的99%都是公开的。
boatcoder 2013年

7
@Red根据HTTP / 1.1标准,这是服务器的故障,而不是浏览器的故障。预计GET在服务器端不会有任何副作用。该标准甚至说:“用户没有要求副作用,因此不能对它们负责。”
eyuelt 2014年

45

在REST中应该没有会话,因此没有任何破坏。REST客户端对每个请求进行身份验证。登录或注销,这只是一种幻想。

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

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


实地研究论文- 第5.1.3节

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


1
我实际上并不知道这一点。然后,我猜我的应用程序根本就不会非常RESTful,因为我使用的是带有FormsAuthentication的ASP.NET MVC,它依赖于会话...
Daniel Liuzzi 2010年

19
但实际上,登录信息保存在标记有httponly属性的cookie中,以防止某些xss风险,这意味着只能从服务器上重置它(
除非

6
用户中的“手动”进入浏览器设置,然后选择“清除cookie”选项。几乎无法接受的“注销”网站的方法。
雷木斯·鲁萨努

1
@Remus Ahhh,杰出的Web浏览器如何使编写Web应用程序如此痛苦。
Darrel Miller 2010年

1
@DarrelMiller是的,但是不撤消服务器端的JWT是一个安全漏洞。即使令牌未存储在服务器上,当用户注销/更改密码/更改角色/退出/等时,也应将其列入黑名单,以防止滥用(至少在它们过期之前)。
java-addict301

38

一种GET可能被滥用的方法是,一个人(竞争对手可能是:)src="<your logout link>"在互联网上放置了一个带有“任何位置”的图片标签,如果您网站的用户偶然发现了该页面,他将在不知不觉中被注销。


4
不,这是不对的。仅当发送了正确的cookie数据(而不是来自其他域)时,注销链接才有效。而且即使会话ID存储在url中,也不会起作用,因为每个会话的这些更改都会改变。
理查德H

4
哇,我没想到!因此,另一个原因是不使用GET,另一个原因是我不明白为什么每个人都这样做。该死的,现在我很想在我的帖子中也包含一个stackoverflow.com/users/logout “图像”,看看会发生什么:-D
Daniel Liuzzi,2010年

24
src =是一个简单的浏览器请求,它不是来自服务器端,而是来自客户端。它携带所有cookie,并来自用户IP。这就是为什么广告跟踪像素有效的原因。确定此类攻击的唯一方法是检查引荐来源。
拉韦伦

12
SuperLogout.com正是这样做的(/logout在隐藏图像中加载URL),并且可以正常工作。
Dan Dascalescu

9
re:SuperLogout ...我不知道为什么点击那个。
MI Wright

21

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

使用[host name]\bla bla\logout中不是一个真正的REST全路(IMO)的URL,那么为什么关于正确使用GET上/ POST的争论?

当然,我也使用GET到我的应用程序中的注销URL :-)


2
在那种情况下,我会辩称在URL中包含[用户名]部分似乎是不必要的,因为用户总是从自己的会话中注销(即DELETE)。从来没有其他用户':-)
Daniel Liuzzi 2010年

1
并非完全如此-我们是说会话是一种资源,我们希望将其删除。因此,为了统一寻址任何会话,您需要将用户名作为URL的一部分。您的论点就像说对[photo gallary] \ pictures发出PUT动作一样好,表示您正在添加照片(在[photo gallary] \ [user name] \ pictures中可用)。必须明确处理不同的资源,其中不能有任何隐含性。该网站可能允许其他用户向您的画廊添加图片-这将是访问控制的一部分,就像您可以拥有可以杀死任何用户会话的超级用户一样。
VinayC,2010年

1
从哲学上讲,您可以将会话和照片称为“资源”,但实际上,我不会将它们视为相同的资源。会话始终本质上仅限于当前用户(因此命名为“会话”),并且至少在ASP.NET中,无法访问其他用户的会话。甚至应用程序开发人员也没有直接枚举所有活动会话的方法,也没有办法单独终止会话。您可以重新启动应用程序以终止所有会话(InProc),但是我不会调用该访问控制。除了URL,问题仍然存在:GET还是POST?
Daniel Liuzzi 2010年

资源,因此其地址(URL)是REST的重要组成部分。因此,如果按照我说的那样选择URL,则DELETE成为正确的单词-而不是GET或POST。同样,即使您将自己限制为使用ASP.NET,也始终可以使用自定义状态提供程序,该提供程序可以让您枚举会话中的所有内容,并在需要时终止其他会话。对于开箱即用的进程内会话,应该对global.asax有所了解,才能为您提供功能。确实是是否需要这种功能的问题。对于很少的需求,人们倾向于重新启动网站以将人们赶出网站。
VinayC,2010年

这对我来说最有意义。为Web API提供会话路由,并在其上调用DELETE。是那个../session或../session/current。Thankx @VinayC
Simon Hooper

16

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


有趣; 我从来没有这样想过。+1。
斯特拉格

可以想象,这取决于应用程序(某种“级联删除”行为),但是您是对的。
Andres Jaan Tack 2010年

@JoelEtherton谢谢Joel,我正在阅读答案,想知道何时才能找到正确的答案。:)
Kirill Fuchs 2015年

4
这很混乱,因为注销更改状态。POST是用于更改状态的动词。GET用于获取无状态数据。这令人困惑,因为我们期望POST请求具有有效负载。如下所述,对于会话对象,DELETE最正确。
Michael Cole 2015年

1
@MichaelCole:我同意POST和GET之间的困难。不过,我不同意DELETE动词的用法。DELETE用于处理资源,从这个意义上讲,会话不是资源。考虑一下,如果您可以删除它,那么您也应该可以将其放入。
乔尔·埃瑟顿

16

您好,我的观点是,登录时检查用户名/密码,如果匹配,则创建登录令牌。

CREAT令牌=>方法POST

注销时,您会破坏令牌,因此对我而言,最合乎逻辑的方法应该是DELETE

DELETE令牌=>方法DELETE


4
有趣的角度。
Drumbeg

1
我在Spring Boot REST应用程序中使用了该方法。
Please_Dont_Bully_Me_SO_Lords '18

1
语义上正确。我同意...
DAG

1

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

也许链接可以用javascript实现?

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


谢谢。该数据库的状态不会改变,但会话状态会。我看到的唯一问题是我在问题中提到的有关用户被踢出的问题。无损,但很烦人。我通常也会遵循“如果大家伙做到这一点,那一定是可以的”的口头禅。我只是想知道其他人对此有何看法。
Daniel Liuzzi 2010年

0

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


您能否详细说明“注销脚本”?我不确定您是否要设置Cookie到期时间(这并不能消除让用户手动注销的方法的必要。)
Daniel Liuzzi 2010年

注销脚本将结束用户(实际上是浏览器)调用它的会话。在ASP.net中,会话是可以放弃的服务器端对象。PHP具有类似的系统。由于该浏览器调用了结束会话的脚本,因此它已经知道要结束的脚本,从而无需POST或GET变量。
罗布

1
是的,我现在得到你。我已经有了脚本,特别是FormsAuthentication.SignOut(),但是我的问题是关于如何调用脚本(如GET或POST)。
Daniel Liuzzi 2010年

哦,您有格式的网址吗?您是否不传递任何信息都没有关系。可能发生的最糟糕的事情是有人手动打开脚本,将自己注销。如果没有必要,我什至不会将其设置为表单字段,指向脚本的链接也可以正常工作。如果您确实要向脚本发送信息,我可能会去POST,以便不向用户显示任何信息(除非他们查看页面源代码),如果他们刷新了,他们将从浏览器中得到警告。 (页面已过期),这可能是可取的。
罗布

0

最近,我正在使用GET进行注销的项目中,下面是Nodejs Express中的代码,它工作得很好

您的router.js

const express = require("express");
router.get("/signout", signout);

您的controller.js

exports.signout  = (req, res) => {
        res.clearCookie('t'); //clearing cookie, which is 
            //assign to the user during sign in.          
            res.json({message : 'Signout success'});   
        };

-2

我看不出注销(提升用户权限)是一种破坏性的行为。那是因为“登出”操作仅对已经登录的用户可用,否则将过时。

浏览器cookie中包含的随机生成的字符串都代表您的用户会话。有无数种方法可以销毁它,因此有效注销只是对访客的一种服务。


2
wget我实际上不得不做一次在蜘蛛模式下使用私有Wiki上正确的会话cookie的事情。当然,最早的抓取网址之一是/logout
Helgi,

5
尝试访问SuperLogout.com,以查看对/logout页面的破坏性GET请求的真实情况。例如,您必须重新登录Gmail,重新登录聊天,在您滚动的所有环聊对话中找到自己的位置等。-这仅适用于Google.com。
Dan Dascalescu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.