【问题标题】:Correct way to delete cookies server-side删除cookie服务器端的正确方法
【发布时间】:2011-07-14 05:51:42
【问题描述】:

对于我的身份验证过程,我会在用户登录时创建一个唯一令牌并将其放入用于身份验证的 cookie 中。

所以我会从服务器发送这样的东西:

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/;

适用于所有浏览器。然后为了删除一个 cookie,我发送了一个类似的 cookie,其中 expires 字段设置为 1970 年 1 月 1 日

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/; expires=Thu, Jan 01 1970 00:00:00 UTC; 

这在 Firefox 上运行良好,但不会删除 IE 或 Safari 上的 cookie。

那么删除 cookie 的最佳方法是什么(最好不使用 JavaScript)? set-the-expires-in-the-past 方法似乎很笨重。还有为什么这在 FF 中有效,但在 IE 或 Safari 中无效?

【问题讨论】:

标签: http cookies


【解决方案1】:

发送带有; expires 的相同cookie 值不会破坏cookie。

通过设置一个空值来使 cookie 失效,并同时包含一个 expires 字段:

Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT

请注意,您不能强制所有浏览器删除 cookie。客户端可以配置浏览器以使 cookie 持续存在,即使它已过期。如上所述设置值将解决此问题。

【讨论】:

  • 我建议使用空文本作为垃圾,而不是 "deleted",以避免以后混淆潜在的合法值等于“已删除”
  • @raulk 是的,你是对的。有趣的是它以前没有被注意到,希望它不会引起太大的问题。 yegor256,一个空值应该在大多数情况下工作。相关:有些人可能想知道为什么即使在发送此标头后他们的 cookie 也不会被删除。在这种情况下,请查看来自其他域的 cookie。比如删除foo=bar; domain=www.example.com后,会使用另一个cookiefoo=qux; domain=.example.com
  • "客户端可以配置浏览器,使 cookie 持续存在,即使它已过期。如上所述设置值将解决此问题。"难道客户端不能将浏览器配置为忽略您将 cookie 内容也设置为“已删除”的请求吗?你无法强迫客户做任何它不想做的事情。
  • @Ajedi32 可以,但是您必须付出额外的努力才能这样做(作为客户)。忽略空值的行为更为常见,浏览器忽略此类请求是没有意义的,尤其是对于无效的会话 ID。
  • -1 因为我从未见过将浏览器配置为忽略 cookie 过期的方法,并且我不相信存在任何提供此类选项的浏览器。更重要的是,在@DaveJarvis 相当大胆的编辑之后,您回答的第一句话现在对于任何主要浏览器或任何符合规范的用户代理都是完全错误的。 tools.ietf.org/search/rfc6265#section-5.3 规定 “如果在任何时候 cookie 存储中存在过期的 cookie,用户代理必须从 cookie 存储中逐出所有过期的 cookie。” 据我所知,这是实际上每个浏览器都做了什么。
【解决方案2】:

在我撰写此答案时,accepted answer to this question 似乎表明,当接收到 Expires 值已过去的替换 cookie 时,浏览器不需要删除 cookie。这种说法是错误的。将 Expires 设置为过去是删除 cookie 的标准、符合规范的方式,规范要求用​​户代理尊重它。

过去使用Expires 属性删除cookie 是正确的,并且是规范规定的删除cookie 的方法。 RFC 6255 的示例部分指出:

最后,为了移除一个cookie,服务器返回一个Set-Cookie header 过期日期在过去。服务器会成功 仅当 Path 和 Domain 属性在 Set-Cookie 标头匹配 cookie 时使用的值 已创建。

用户代理要求部分包括以下要求,如果用户代理收到一个同名的新 cookie,其到期日期在过去

  1. 如果 [当接收到新 cookie 时] cookie 存储包含与新创建的 cookie 具有相同名称、域和路径的 cookie:

    1. ...
    2. ...
    3. 更新新创建的 cookie 的创建时间以匹配旧 cookie 的创建时间。
    4. 从 cookie 存储中删除旧 cookie。
  2. 将新创建的 cookie 插入 cookie 存储区。

如果 cookie 的过期日期已过,则该 cookie 已“过期”。

用户代理必须从 cookie 存储中清除所有过期的 cookie 如果在任何时候,cookie 存储中存在过期的 cookie。

以上第 11-3、11-4 和 12 点共同表示,当接收到具有相同名称、域和路径的新 cookie 时,必须删除旧 cookie 并用新 cookie 替换。最后,关于过期 cookie 的以下要点进一步表明,在完成之后,new cookie 必须立即被驱逐。在这一点上,该规范没有为浏览器提供任何回旋余地。如果浏览器向用户提供禁用 cookie 过期的选项,正如公认的答案所暗示的某些浏览器所做的那样,那么它将违反规范。 (这样的功能也没什么用,据我所知,它在任何浏览器中都不存在。)

那么,为什么这个问题的 OP 观察到这种方法失败了?虽然我没有清理 Internet Explorer 的副本来检查其行为,但我怀疑这是因为 OP 的 Expires 值格式错误!他们使用了这个值:

expires=Thu, Jan 01 1970 00:00:00 UTC;

但是,这在两种情况下在语法上是无效的。

规范的syntax section 规定Expires 属性的值必须是a

rfc1123-date,定义在[RFC2616], Section 3.3.1

按照上面的第二个链接,我们发现这是作为格式示例给出的:

Sun, 06 Nov 1994 08:49:37 GMT

并找到语法定义...

  1. 要求日期以日月年格式书写,而不是提问者使用的月日年格式。

具体来说,它定义rfc1123-date如下:

```
rfc1123-date = wkday "," SP date1 SP time SP "GMT"
```

并像这样定义date1

```
date1        = 2DIGIT SP month SP 4DIGIT
             ; day month year (e.g., 02 Jun 1982)
```

  1. 不允许 UTC 作为时区。

规范包含以下关于此格式可接受的时区偏移量的声明:

所有 HTTP 日期/时间戳必须以格林威治标准时间 (GMT) 表示,无一例外。

更重要的是,如果我们深入研究这种日期时间格式的原始规范,我们会发现在 https://www.rfc-editor.org/rfc/rfc822 的初始规范中,Syntax section 将“UT”(意思是“通用时间”)列为可能的值,但 not 是否将 UTC(协调世界时)列为有效。据我所知,在这种日期格式中使用“UTC”从来没有有效;在 1982 年首次指定格式时,它不是一个有效值,并且 HTTP 规范通过禁止使用除 "格林威治标准时间”。

如果这里的提问者使用了Expires 属性,例如this,那么:

expires=Thu, 01 Jan 1970 00:00:00 GMT;

那么它可能会起作用。

【讨论】:

  • 一种更简洁的方法是生成这样的字符串。例如在 JS 中:new Date(0).toUTCString()
  • 用户代理可以选择不遵循“必需”规则。接受的答案只是说明理论上,浏览器可能会继续发送过期的 cookie。
  • 我的错误是没有包含与创建时相同的路径“/”。谢谢
【解决方案3】:

将“过期”设置为过去的日期是删除 cookie 的标准方法。

您的问题可能是因为日期格式不常规。 IE 可能只需要 GMT。

【讨论】:

    【解决方案4】:

    使用 最大年龄=-1 而不是“过期”。它更短,对语法不那么挑剔,而且 Max-Age 优先于 Expires。

    【讨论】:

    【解决方案5】:

    对于 GlassFish Jersey JAX-RS 实现,我已经通过通用方法解决了这个问题,即描述所有通用参数。至少三个参数必须相等:name(="name")、path(="/") 和 domain(=null):

    public static NewCookie createDomainCookie(String value, int maxAgeInMinutes) {
        ZonedDateTime time = ZonedDateTime.now().plusMinutes(maxAgeInMinutes);
        Date expiry = time.toInstant().toEpochMilli();
        NewCookie newCookie = new NewCookie("name", value, "/", null, Cookie.DEFAULT_VERSION,null, maxAgeInMinutes*60, expiry, false, false);
        return newCookie;
    }
    

    并使用常用的方式设置cookie:

    NewCookie domainNewCookie = RsCookieHelper.createDomainCookie(token, 60);
    Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();
    

    并删除 cookie:

    NewCookie domainNewCookie = RsCookieHelper.createDomainCookie("", 0);
    Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();
    

    【讨论】:

    • 对我来说,当我将 maxAge 设置为 0 时,它会输出一个 Max-Age=0 的 cookie,Chrome 似乎会忽略它。在RFC 6265 section 4.1.1 中,它将 Max-Age 的语法指定为“非零位”。这可能是原因。尽管正如@JoshC13 所提到的,第 5.2.2 节确实讨论了解释小于或等于零的值。所以它有点自相矛盾......
    • 我不知道细节,但这些值在 Chrome 和其他浏览器中确实有效:maxAgeInMinutes*60, expiry.
    • @MatthijsWessels 好收获!我挖得更深了,明显的矛盾实际上是故意的,正如rfc-editor.org/errata/eid3430 的勘误表中所述。为了“最大化互操作性”,用户代理需要将零或负的Max-Age解释为最早可表示的日期和时间,但服务器被禁止发送这样的Max-Age 值。我猜作者知道无法处理 Max-Age=0 的现有客户端和在他们编写规范时发送它的服务器,并试图从两端缓解问题。
    • @Crimean.us 我也不能再复制了。也许我做错了什么
    • @MatthijsWessels 在我的示例中,通过将到期日期设置为 ZonedDateTime.now().plusMinutes(maxAgeInMinutes),解决了忽略 Max-Age=0 的问题。对于 maxAgeInMinutes=0,它是当前日期时间。这段代码在真实的web应用中运行了很长时间。
    猜你喜欢
    • 2015-05-06
    • 2017-03-23
    • 2019-04-30
    • 2014-09-17
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    • 2011-07-09
    相关资源
    最近更新 更多