我正在为 asp.net MVC 应用程序使用自定义表单身份验证,并且在某些用户似乎没有 cookie 时遇到问题。我们使用的自定义表单身份验证方法与此类似——自定义表单身份验证。本质上,我们创建了一个自定义的 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;
网络配置
<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 引用通常设置为登录页面,这意味着用户可能不会在没有登录的情况下访问违规页面(至少在某些时候) - 会话变量的状态似乎支持该假设
- 我经常连续看到多个这些错误(相隔一分钟左右) - 暗示重复登录尝试无法解决问题(不知道为什么会这样)
- 出现此问题的用户似乎继续遇到此问题。换句话说,它似乎不是“抽奖”——而是用户帐户(cookie 长度会话变量暗示它正在正确序列化)或客户端浏览器的某些东西。
- 我听说至少有一个用户能够在移动设备上登录,但不能在他们的桌面上登录
- 该站点总共可能使用了 10 个左右的 cookie(包括所有已添加的各种测试 cookie)——在添加测试 cookie 之前,它使用了大约 4 个,包括 auth cookie。此外,当错误发生时,请求中通常只有 2 或 3 个 cookie,所以我认为 cookie 的数量不是问题。
在这一点上,我愿意尝试几乎任何事情。我尝试使用存储在会话中的自定义身份作为备份进行设置,但无法使其正常工作,因此即使有人对如何实现它有想法(如果可能),我将不胜感激(如果这是题外话我可以删除它)。
对不起,文字的墙壁,但我只是想指出我们已经调查并最有可能排除的所有潜在问题。
编辑
看来可能还有另一个潜在的相关问题。我看到错误日志让我相信某些身份的“IsAuthenticated”属性在不应该设置为 false 时被设置为 false。我们确实将其初始化为 false,并在用户回答安全问题后将其设置为 true。当我们将其设置为 true 时,它应该更新原理和身份验证票证/cookie。我不确定这是否是因为我如何反序列化自定义主体的一些问题。