我有一个使用表单身份验证的ASP.NET Web应用程序。我们在10月25日凌晨1点至凌晨2点(在英国,夏令时结束于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个小时到凌晨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(错误)

DST发布结束:
服务器时间:01:35:06
DateTime.Now = 01:35:06
ticket.Expiration = 02:15:06(正确)

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

任何想法为什么会这样?代码问题?服务器问题?

最佳答案

问题的症结在这里:

if (ticket.Expiration < DateTime.Now)


将此更改为:

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


或对此更好,它在内部做同样的事情:

if (ticket.Expired)


问题实际上是详细地in the comments found in the MSDN reference sources详细描述的。

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

另外,我通常建议不要使用ToUniversalTime,但是在这种情况下,可以。

关于c# - 奇怪的表单例份验证到期行为导致DST结束,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33567912/

10-13 09:05