ASP.NET 身份 2 和匿名用户
ASP.NET Identity 2 and Anonymous Users
在我们开发的电子商务解决方案中,我们使用的是 AspNet Identity 2.2.1,并且要求所有来宾(匿名)用户无需事先注册网站即可完成结帐。为了满足这个要求,编写了一个名为 UserMigrationAttribute 的 ActionFilter,它从 cookie 中获取 SessionTrackId(字符串 GUID)——如果没有找到 SessionTrackId 和请求 cookie,我们从 HttpModule 为每个请求设置它——并在数据库中创建和实际的 IdentityUser用户名类似于 SessionTrackId@mydomain.com.
我们用这个 UserMigration 属性修饰了我们的 BaseController class,以便在整个站点中使用它的功能。
到目前为止,一切都按预期工作,只有一个缺点,即当页面第一次为任何用户加载时,如果我们尝试制作 Jquery Ajax调用具有 [ValidateAntiForgeryToken]
属性的方法,调用失败并出现“The provided anti-forgery token was meant for a different claims-based user than the current user.
”错误,即使我们在每个 ajax 调用中发送 __RequestVerificationToken
参数也是如此。
但是如果用户通过单击 link and/or reloads/refreshes 当前页面打开另一个页面,所有后续 ajax 调用都会成功完成。
根据我们的理解,UserMigrationAttribute 在 OnActionExecuting 方法上创建用户,但是在我们登录用户之后,进程中的@Html.AntiForgeryToken() 没有被更新为正确的值。
您可以在下面找到 UserMigrationAttribute 代码;
[AttributeUsage(AttributeTargets.Class)]
public class UserMigrationAttribute : ActionFilterAttribute
{
public ApplicationSignInManager SignInManager(ActionExecutingContext filterContext)
{
return filterContext.HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
public UserManager UserManager(ActionExecutingContext filterContext)
{
return filterContext.HttpContext.GetOwinContext().GetUserManager<UserManager>();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
CreateMigrateCurrentUser(filterContext);
base.OnActionExecuting(filterContext);
}
private static readonly object LockThis = new object();
private void CreateMigrateCurrentUser(ActionExecutingContext filterContext)
{
lock (LockThis)
{
var signInManager = SignInManager(filterContext);
var userManager = UserManager(filterContext);
var sessionTrackId = GetSessionTrackId(filterContext);
if (!filterContext.HttpContext.Request.IsAuthenticated)
{
if (!string.IsNullOrEmpty(sessionTrackId))
{
var username = string.Format("{0}@mydomain.com", sessionTrackId);
var user = userManager.FindByName(username);
if (user == null)
{
user = new User() {UserName = username, Email = username};
var result = userManager.Create(user);
userManager.AddToRole(user.Id, StringResources.AnonymousVisitorsGroup);
}
signInManager.SignIn(user, true, true);
}
}
else
{
if (!string.IsNullOrEmpty(sessionTrackId))
{
var username = string.Format("{0}@mydomain.com", sessionTrackId);
var user = userManager.FindByName(username);
if (user != null)
{
if (!HttpContext.Current.User.IsInRole(StringResources.AnonymousVisitorsGroup))
{
var targetUserId = HttpContext.Current.User.Identity.GetUserId<int>();
var service = new Service();
service.Users.MigrateUser(user.Id, targetUserId);
}
}
}
}
}
}
private string GetSessionTrackId(ActionExecutingContext filterContext)
{
var retVal = string.Empty;
if (filterContext.HttpContext.Request.Cookies["stid"] != null)
{
retVal = filterContext.HttpContext.Request.Cookies["stid"].Value;
}
return retVal;
}
}
非常感谢任何帮助或建议。
谢谢,
发生这种情况是因为防伪令牌设置在 cookie 中,直到下一次请求才会更新。如果您手动让用户登录,您还应该发出重定向(即使到他们已经前往的同一页面),只是为了确保 cookie 数据正确。这通常是自然发生的,因为登录表单将在用户登录后重定向到需要授权的 URL,从而解决了这个问题。由于您目前没有重定向,因此数据不同步。
但是,我不得不说,对于这个特定用例,这似乎是一个非常糟糕的解决方案。创建某种临时类型的用户并让该用户登录以处理来宾结帐,充其量会在您的数据库中创建不必要的大量无用数据,最坏的情况是会导致错误和您遇到的其他问题。
我也 运行 一个电子商务网站,我们处理访客结帐的方式非常简单。结帐数据仅存储在会话中(电子邮件、shipping/billing 地址等)。我们构建了一个视图模型来处理实际结帐,其中提交销售所需的数据来自用户对象(如果他们已登录)或这些会话变量(如果他们未登录)。如果用户既没有登录,也没有设置必要的会话变量,那么他们将被重定向到收集 billing/shipping 等信息的入职表单。
对于维护匿名购物车等其他方面,我们使用带有购物车标识符的永久 cookie。如果用户最终创建了一个帐户,我们会将匿名购物车与其用户相关联,然后删除 cookie。这确保他们的购物车在会话超时和关闭浏览器之类的事情之后仍然存在,即使他们是匿名的。
换句话说,在所有这些事情中,实际上不需要用户对象。如果它在那里(用户已登录),很好,我们将使用它。否则,我们会通过其他方式收集并保留结帐所需的信息。
在我们开发的电子商务解决方案中,我们使用的是 AspNet Identity 2.2.1,并且要求所有来宾(匿名)用户无需事先注册网站即可完成结帐。为了满足这个要求,编写了一个名为 UserMigrationAttribute 的 ActionFilter,它从 cookie 中获取 SessionTrackId(字符串 GUID)——如果没有找到 SessionTrackId 和请求 cookie,我们从 HttpModule 为每个请求设置它——并在数据库中创建和实际的 IdentityUser用户名类似于 SessionTrackId@mydomain.com.
我们用这个 UserMigration 属性修饰了我们的 BaseController class,以便在整个站点中使用它的功能。
到目前为止,一切都按预期工作,只有一个缺点,即当页面第一次为任何用户加载时,如果我们尝试制作 Jquery Ajax调用具有 [ValidateAntiForgeryToken]
属性的方法,调用失败并出现“The provided anti-forgery token was meant for a different claims-based user than the current user.
”错误,即使我们在每个 ajax 调用中发送 __RequestVerificationToken
参数也是如此。
但是如果用户通过单击 link and/or reloads/refreshes 当前页面打开另一个页面,所有后续 ajax 调用都会成功完成。
根据我们的理解,UserMigrationAttribute 在 OnActionExecuting 方法上创建用户,但是在我们登录用户之后,进程中的@Html.AntiForgeryToken() 没有被更新为正确的值。
您可以在下面找到 UserMigrationAttribute 代码;
[AttributeUsage(AttributeTargets.Class)]
public class UserMigrationAttribute : ActionFilterAttribute
{
public ApplicationSignInManager SignInManager(ActionExecutingContext filterContext)
{
return filterContext.HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
public UserManager UserManager(ActionExecutingContext filterContext)
{
return filterContext.HttpContext.GetOwinContext().GetUserManager<UserManager>();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
CreateMigrateCurrentUser(filterContext);
base.OnActionExecuting(filterContext);
}
private static readonly object LockThis = new object();
private void CreateMigrateCurrentUser(ActionExecutingContext filterContext)
{
lock (LockThis)
{
var signInManager = SignInManager(filterContext);
var userManager = UserManager(filterContext);
var sessionTrackId = GetSessionTrackId(filterContext);
if (!filterContext.HttpContext.Request.IsAuthenticated)
{
if (!string.IsNullOrEmpty(sessionTrackId))
{
var username = string.Format("{0}@mydomain.com", sessionTrackId);
var user = userManager.FindByName(username);
if (user == null)
{
user = new User() {UserName = username, Email = username};
var result = userManager.Create(user);
userManager.AddToRole(user.Id, StringResources.AnonymousVisitorsGroup);
}
signInManager.SignIn(user, true, true);
}
}
else
{
if (!string.IsNullOrEmpty(sessionTrackId))
{
var username = string.Format("{0}@mydomain.com", sessionTrackId);
var user = userManager.FindByName(username);
if (user != null)
{
if (!HttpContext.Current.User.IsInRole(StringResources.AnonymousVisitorsGroup))
{
var targetUserId = HttpContext.Current.User.Identity.GetUserId<int>();
var service = new Service();
service.Users.MigrateUser(user.Id, targetUserId);
}
}
}
}
}
}
private string GetSessionTrackId(ActionExecutingContext filterContext)
{
var retVal = string.Empty;
if (filterContext.HttpContext.Request.Cookies["stid"] != null)
{
retVal = filterContext.HttpContext.Request.Cookies["stid"].Value;
}
return retVal;
}
}
非常感谢任何帮助或建议。
谢谢,
发生这种情况是因为防伪令牌设置在 cookie 中,直到下一次请求才会更新。如果您手动让用户登录,您还应该发出重定向(即使到他们已经前往的同一页面),只是为了确保 cookie 数据正确。这通常是自然发生的,因为登录表单将在用户登录后重定向到需要授权的 URL,从而解决了这个问题。由于您目前没有重定向,因此数据不同步。
但是,我不得不说,对于这个特定用例,这似乎是一个非常糟糕的解决方案。创建某种临时类型的用户并让该用户登录以处理来宾结帐,充其量会在您的数据库中创建不必要的大量无用数据,最坏的情况是会导致错误和您遇到的其他问题。
我也 运行 一个电子商务网站,我们处理访客结帐的方式非常简单。结帐数据仅存储在会话中(电子邮件、shipping/billing 地址等)。我们构建了一个视图模型来处理实际结帐,其中提交销售所需的数据来自用户对象(如果他们已登录)或这些会话变量(如果他们未登录)。如果用户既没有登录,也没有设置必要的会话变量,那么他们将被重定向到收集 billing/shipping 等信息的入职表单。
对于维护匿名购物车等其他方面,我们使用带有购物车标识符的永久 cookie。如果用户最终创建了一个帐户,我们会将匿名购物车与其用户相关联,然后删除 cookie。这确保他们的购物车在会话超时和关闭浏览器之类的事情之后仍然存在,即使他们是匿名的。
换句话说,在所有这些事情中,实际上不需要用户对象。如果它在那里(用户已登录),很好,我们将使用它。否则,我们会通过其他方式收集并保留结帐所需的信息。