.net mvc一些用户缺少cookie

时间:2014-04-18 21:40:59

标签: asp.net asp.net-mvc cookies authorization missing-cookies

我正在为asp.net MVC应用程序使用自定义表单身份验证,并且遇到某些用户看似没有cookie的问题。我们使用的自定义表单身份验证方法与此类似 - custom forms authentication。本质上,我们创建一个自定义Principal和Identity,将其序列化,并将其存储在FormsAuthenticationTicket的UserData属性中:

登录

MyCustomPrincipal principal = new MyCustomPrincipal(user);
DateTime expiration = DateTime.Now.AddMinutes(30);

FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                1,
                u.Username,
                DateTime.Now,
                expiration,
                true,
                JsonConvert.SerializeObject(principal));

HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authTicket));
cookie.Expires = expiration;

Response.Cookies.Set(cookie);

然后我们在global.asax的Application_AuthenticateRequest事件中获取auth cookie。

global.asax - Application_AuthenticateRequest

// Get the authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];

// If the cookie can't be found, don't issue the ticket
if (authCookie == null) return;

// Get the authentication ticket and rebuild the principal
// & identity
FormsAuthenticationTicket authTicket =
          FormsAuthentication.Decrypt(authCookie.Value);

MyCustomPrincipal userPrincipal = new MyCustomPrincipal(authTicket.UserData);
DateTime expiration = DateTime.Now.AddMinutes(30);

FormsAuthenticationTicket newAuthTicket = new FormsAuthenticationTicket(
              1,
              ((MyCustomIdentity)userPrincipal.Identity).Username,
              DateTime.Now,
              expiration,
              true,
              JsonConvert.SerializeObject(userPrincipal));



authCookie.Value = FormsAuthentication.Encrypt(newAuthTicket);
authCookie.Expires = expiration;

HttpContext.Current.Response.Cookies.Set(authCookie);

Context.User = userPrincipal;

的web.config

<authentication mode="Forms">
  <forms loginUrl="~/Home/Index" timeout="29" name="MYFORMSAUTH" cookieless="UseCookies"/>
</authentication>

这适用于大多数用户,但是,有些用户似乎没有获得授权Cookie设置。我做了一些测试,为我的Elmah错误日志添加更多信息,看看我是否能找到更多关于这个问题的信息。

首先,我尝试在Login方法中设置authcookie之前和之后设置一些测试cookie。这些cookie在Elmah日志中出现,因此在此方法中添加任何类型的Cookie都无效。 然而,日志中还有其他Cookie,包括ASP.NET_SessionId,谷歌分析cookie,有时甚至还有其他我在应用程序中的其他位置设置的cookie(可能来自之前的会话) )

其次,我尝试从登录操作向会话添加一些信息,如果在下一个操作中找不到authcookie,则将其包含在错误日志中。我包括了cookie的长度(名称的长度+加密值的长度)以及尝试登录的时间。只有在用户的凭据有效且应用程序尝试添加身份验证cookie时,才会添加这两者。我执行在正在生成的错误日志中查看这些值。长度总是大于0,我没有看到任何大约2300,所以我不认为大小是一个问题。尝试登录与发生错误的时间相同 - 因此应该在错误发生之前立即设置会话变量(cookie丢失)。

还有一些注意事项 -

  • 似乎没有任何特别的浏览器似乎导致错误更多(尽管它可能在移动浏览器上发生更多)
  • 同样,该网站似乎适用于绝大多数用户,当然我们无法重现该问题
  • 由于我没有看到测试cookie,我猜测由于某种原因没有当时正在通过登录方法设置cookie(尽管我可以看到其他cookie设置在哪里,这意味着以前成功登录)
  • elmah日志中的http referer通常设置为登录页面,这意味着用户可能没有登录而没有登录(至少在某些时候) - 会话变量的状态似乎支持那个假设
  • 我经常连续看到多个这些错误(相隔一分钟左右) - 暗示重复登录尝试无法解决问题(不确定为什么会这样)
  • 看来有这个问题的用户仍然存在问题。换句话说,它似乎并不是“抽奖运气” - 而是用户的帐户(cookie长度会话变量暗示它正确序列化)或客户端浏览器。
  • 我听说过至少有一位用户能够在移动设备上登录,而不是他们的桌面
  • 总的来说,网站可能会使用10个左右的cookie(包括已添加的所有各种测试cookie) - 在添加测试cookie之前,它使用了大约4个,包括auth cookie。此外,当错误发生时,请求中通常只有2或3个cookie,所以我不认为cookie的数量是个问题。

此时我愿意尝试几乎任何事情。我尝试使用存储在会话中的自定义标识作为备份进行设置,但无法使其正常工作,所以即使有人有关于如何实现它的想法(如果可能的话),也不胜感激(如果这不是主题那么我可以删除它。)

对于文字的墙壁感到抱歉,但我只是想指出我们调查过的所有潜在问题,并且很可能已经排除了这些问题。

修改

似乎可能存在另一个可能相关的问题。我看到错误日志让我相信某些身份的“IsAuthenticated”属性在不应该被设置为false时。我们将此初始化为false,并在用户回答安全问题后将其设置为true。当我们将其设置为true时,它应该更新原则和身份验证票证/ cookie。我不确定是否会发生这种情况,因为我对自定义主体的反序列化存在一些问题。

2 个答案:

答案 0 :(得分:0)

是否启用了服务器端缓存? 我记得我有类似的问题,原因是服务器端缓存(配置错误)和服务器端代码没有执行但客户端到达页面。 另外在我身边有一个错误(在动态页面上启用缓存的iis bug),在某些情况下会话cookie被发送到多个客户端,这会导致意外结果。

这可以解释您的非日志记录行为和客户端上不存在的cookie。

此致

答案 1 :(得分:0)

所以我放弃了并决定使用Session来存储我的主体,并在我看不到身份验证cookie时检查它。我可以通过创建自定义Authorize属性并在那里检查会话来轻松地做到这一点。我还没有把它推到生产中,所以我不能100%肯定这会解决这个问题,但是初步测试表明它就足够了。

<强> CustomAuthorizeAttribute

public class MyCustomAuthorizeAttribute : AuthorizeAttribute
{

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Get the authentication cookie
        string cookieName = FormsAuthentication.FormsCookieName;
        HttpCookie authCookie = HttpContext.Current.Request.Cookies[cookieName];

        // If the cookie can be found, use the base authentication
        if (authCookie != null)
        {
            base.OnAuthorization(filterContext);
        }
        else
        {
            // The cookie is not found, check the session for the principal

            var p = HttpContext.Current.Session[FormsAuthentication.FormsCookieName];

            if (p != null)
            {
                // there is a principal object in the session
                MyCustomPrincipal principal = (MyCustomPrincipal)p;

                HttpContext.Current.User = principal;                   

                // we've loaded the principal, now just do the base authorization
                base.OnAuthorization(filterContext);

            }
            else
            {
                // there is no principal object in the cookie or the session, the user is not authenticated
                HandleUnauthorizedRequest(filterContext);
            }

        }
    }     
}

一旦我们使用自定义authorize属性正确设置了当前主体,我们就可以使用基本授权,因此我们不必担心自己实现该功能。基本授权应检查当前主体并根据该授权进行授权。

我不会将此标记为答案,因为它并没有真正解决潜在的问题,但我认为我会将其作为潜在的解决方法,以防其他人偶然发现类似的问题。