2

In our developing e-commerce solution we are using AspNet Identity 2.2.1 and it is required that any guest (anonymous) users should complete checkout without prior registration to the website. In order to fullfill this requirement have written an ActionFilter named UserMigrationAttribute which obtains SessionTrackId (string GUID) from cookie -which we set from a HttpModule for every request if SessionTrackId is not found along with request cookies- and creates and actual IdentityUser in database with the username something like SessionTrackId@mydomain.com.

We have decorated our BaseController class with this UserMigration attribute in order to utilize its functions throughout the site.

Everything up to this point works as expected with single downside issue, which is when the page is being loaded for the first time for any user, if we try to make an Jquery Ajax Call to a Method which have [ValidateAntiForgeryToken] attribute, the call fails with the 'The provided anti-forgery token was meant for a different claims-based user than the current user.' error, even though we are sending __RequestVerificationToken parameter with every ajax call.

But if user opens another page by clicking link and/or reloads/refreshes current page, all the subsequent ajax calls complete successfully.

In our understanding UserMigrationAttribute creates user on OnActionExecuting method, but after we signIn user in the process @Html.AntiForgeryToken() is not being updated with the right values.

You may find the UserMigrationAttribute code below;

    [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;

        }

    }

Any help or suggestions are highly appreciated.

Thank you,

4

1 回答 1

4

发生这种情况是因为防伪令牌设置在 cookie 中,直到下一个请求才会更新。如果您手动登录用户,您还应该发出重定向(即使他们已经前往同一页面),只是为了确保 cookie 数据是正确的。这通常会自然发生,因为登录表单会在用户登录后重定向到需要授权的 URL,从而解决问题。由于您当前没有重定向,因此数据不同步。

但是,我不得不说,对于这个特定的用例来说,这似乎是一个非常糟糕的解决方案。创建某种临时类型的用户并让该用户登录以处理访客结帐会在您的数据库中创建不必要的过多无用数据,最坏的情况是会导致错误和其他类似您遇到的问题。

我还经营一个电子商务网站,我们处理客人结账的方式非常简单。结帐数据仅存储在会话中(电子邮件、送货/账单地址等)。我们构建了一个视图模型来处理实际的结帐,其中提交销售所需的数据来自用户对象(如果他们已登录)或这些会话变量(如果未登录)。如果用户既没有登录,也没有设置必要的会话变​​量,那么他们将被重定向到收集帐单/运输等的入职表单。

对于维护匿名购物车等其他方面,我们使用带有购物车标识符的永久 cookie。如果用户最终创建了一个帐户,我们会将匿名购物车与他们的用户相关联,然后删除 cookie。这可以确保他们的购物车在会话超时和关闭浏览器之类的事情之后仍然存在,即使他们是匿名的。

换句话说,在所有这些事情中,实际上都不需要用户对象。如果它在那里(用户已登录),很好,我们将使用它。否则,我们会通过其他方式收集并保留结帐所需的信息。

于 2017-08-03T17:19:51.417 回答