我正在为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丢失)。
还有一些注意事项 -
此时我愿意尝试几乎任何事情。我尝试使用存储在会话中的自定义标识作为备份进行设置,但无法使其正常工作,所以即使有人有关于如何实现它的想法(如果可能的话),也不胜感激(如果这不是主题那么我可以删除它。)
对于文字的墙壁感到抱歉,但我只是想指出我们调查过的所有潜在问题,并且很可能已经排除了这些问题。
修改
似乎可能存在另一个可能相关的问题。我看到错误日志让我相信某些身份的“IsAuthenticated”属性在不应该被设置为false时。我们将此初始化为false,并在用户回答安全问题后将其设置为true。当我们将其设置为true时,它应该更新原则和身份验证票证/ cookie。我不确定是否会发生这种情况,因为我对自定义主体的反序列化存在一些问题。
答案 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属性正确设置了当前主体,我们就可以使用基本授权,因此我们不必担心自己实现该功能。基本授权应检查当前主体并根据该授权进行授权。
我不会将此标记为答案,因为它并没有真正解决潜在的问题,但我认为我会将其作为潜在的解决方法,以防其他人偶然发现类似的问题。