奇怪的表单身份验证过期行为导致 DST 结束

本文关键字:DST 结束 过期 表单 身份验证 | 更新日期: 2023-09-27 18:20:47

我有一个使用表单身份验证的 ASP.NET Web 应用程序。我们在 10 月 25 日凌晨 1 点至凌晨 2 点之间在我们的实时网站上目睹了一些奇怪的行为(在英国,DST 在 2015 年 10 月 25 日凌晨 2 点结束,时钟倒退了一个小时(,所有用户都注销了身份验证票证似乎已过期以下代码:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpCookie cookie = Request.Cookies["MyCookie"];
    FormsAuthenticationTicket ticket;
    try
    {
        ticket = FormsAuthentication.Decrypt(cookie.Value);
    }
    catch
    {
        this.RedirectDueToError(ErrorView);
        return;
    }
    if (ticket == null) { return; }
    if (ticket.Expiration < DateTime.Now)
    {
        this.RedirectDueToError(TimeoutView);
    }
}

Web.config 包含:

<authentication mode="Forms">
    <forms name="MyCookie" timeout="40" requireSSL="false"/>
</authentication>

我已经在测试服务器上重现了该问题,可以看到问题发生在 DST 结束的凌晨 1 点到凌晨 2 点之间。当时间到达凌晨 2 点并自动更改回一小时到凌晨 1 点时,问题就解决了。

在我的测试过程中,我添加了一些额外的日志记录来确定故障点(也是在最初创建身份验证票证之后(ticket.ExpirationDateTime.Now的值。结果是:

在 DST 结束之前:
服务器时间:00:53:39
DateTime.Now = 00:53:39
ticket.Expiration = 01:33:37(正确(

在 DST 结束之前:
服务器时间:01:25:08
DateTime.Now = 01:25:08
ticket.Expiration = 01:05:08(错误(

夏令时结束后:
服务器时间:01:35:06
DateTime.Now = 01:35:06
ticket.Expiration = 02:15:06(正确(

因此,似乎在 DST 结束前一小时内,从 FormsAuthenticationTicket Expiration 属性返回的时间晚了一个小时。我知道过期时间在内部存储为 UTC,但 Expiration 属性是本地时间,并且怀疑转换存在问题。

知道为什么会这样吗?代码问题?服务器问题?

奇怪的表单身份验证过期行为导致 DST 结束

问题的症结就在这里:

if (ticket.Expiration < DateTime.Now)

将其更改为:

if (ticket.Expiration.ToUniversalTime() < DateTime.UtcNow)

或者更好的是,它在内部做同样的事情:

if (ticket.Expired)

该问题实际上在 MSDN 参考源中找到的注释中详细描述。

基本上,它归结为DateTime结构的设计。 比较两个DateTime对象仅考虑其Ticks值,而不考虑其Kind,因此不考虑DST或时区。

另外,我通常建议不要使用 ToUniversalTime ,但在这种特殊情况下,没关系。