В нашем разрабатываемом решении для электронной коммерции мы используем AspNet Identity 2.2.1, и требуется, чтобы все гостевые (анонимные) пользователи завершили оформление заказа без предварительной регистрации на веб-сайте. Чтобы выполнить это требование, мы написали ActionFilter с именем UserMigrationAttribute, который получает SessionTrackId (строковый GUID) из файла cookie, который мы устанавливаем из HttpModule для каждого запроса, если SessionTrackId не найден вместе с файлами cookie запроса, и создает актуальный IdentityUser в базе данных с помощью имя пользователя вроде SessionTrackId@mydomain.com.

Мы украсили наш класс BaseController этим атрибутом UserMigration, чтобы использовать его функции на всем сайте.

Все до этого момента работает, как ожидалось, с единственной проблемой, связанной с обратной стороной: когда страница загружается в первый раз для любого пользователя, если мы попытаемся выполнить JQuery Ajax-вызов метода с атрибутом [ValidateAntiForgeryToken], вызов завершается с ошибкой 'The provided anti-forgery token was meant for a different claims-based user than the current user.', хотя мы отправляем параметр __RequestVerificationToken с каждым вызовом ajax.

Но если пользователь открывает другую страницу, щелкнув ссылку и / или перезагружая / обновляя текущую страницу, все последующие вызовы 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;

        }

    }

Любая помощь или предложения высоко ценятся.

Спасибо,

2
Onur Buyukcaglar 3 Авг 2017 в 17:54

1 ответ

Лучший ответ

Это происходит потому, что маркер защиты от подделки установлен в файле cookie, который не будет обновлен до следующего запроса. Если вы вручную регистрируете пользователя, вам также следует выполнить перенаправление (даже если на ту же страницу, на которую он уже был направлен), просто чтобы убедиться, что данные cookie верны. Обычно это происходит естественным образом, поскольку форма входа перенаправляет на URL-адрес, требующий авторизации, после того, как пользователь вошел в систему, тем самым устраняя проблему. Поскольку в настоящее время вы не выполняете перенаправление, данные не синхронизированы.

Однако я должен сказать, что это кажется очень плохим решением для данного конкретного случая использования. Создание какого-то временного пользователя и вход этого пользователя для обработки гостевой проверки в лучшем случае создает ненужный избыток бесполезных данных в вашей базе данных, а в худшем - приводит к ошибкам и другим проблемам, подобным той, с которой вы столкнулись.

У меня также есть сайт электронной коммерции, и то, как мы обработали гостевую оплату, невероятно упрощено. Данные оформления заказа просто сохраняются в сеансе (электронная почта, адрес доставки / выставления счетов и т. Д.). Мы создаем модель представления для обработки фактической проверки, когда данные, необходимые для отправки продажи, поступают либо от объекта пользователя, если он вошел в систему, либо от этих переменных сеанса, если это не так. Если пользователь не вошел в систему и не установил необходимые переменные сеанса, он перенаправляется в форму подключения, где собираются данные о выставлении счетов / доставке и т. Д.

Для других аспектов, таких как поддержание анонимной корзины, мы используем постоянный файл cookie с идентификатором корзины. Если пользователь в конечном итоге создает учетную запись, мы связываем анонимную корзину с его пользователем, а затем удаляем файл cookie. Это гарантирует, что их корзина выживет после тайм-аута сеанса и таких вещей, как закрытие браузера, даже если они анонимны.

Другими словами, во всех этих вещах на самом деле не нужен пользовательский объект. Если он там (пользователь авторизован), отлично, мы его воспользуемся. В противном случае мы собираем и сохраняем необходимую информацию для оформления заказа другими способами.

4
Chris Pratt 3 Авг 2017 в 20:19
Спасибо за ваш ответ и руководство, это принятый ответ, поскольку он предлагает (само) перенаправление на filterContext, тем самым решая нашу проблему.
 – 
Onur Buyukcaglar
5 Авг 2017 в 16:28