Используя членство и профиль ASP.NET с MVC, как я могу создать пользователя и установить его в HttpContext.Current.User?

Я реализовал собственный объект профиля в коде, как описано здесь Joel:

Как назначить значения профиля?

Я не могу заставить его работать, когда я создаю нового пользователя. Когда я это сделаю:

Membership.CreateUser(userName, password);
Roles.AddUserToRole(userName, "MyRole");

пользователь создается и добавляется к роли в базе данных, но HttpContext.Current.User по-прежнему пуст, а Membership.GetUser() возвращает значение null, поэтому это (из кода Joel) не работает:

static public AccountProfile CurrentUser
{
    get { return (AccountProfile)
                     (ProfileBase.Create(Membership.GetUser().UserName)); }
}

AccountProfile.CurrentUser.FullName = "Snoopy";

Я попытался позвонить Membership.GetUser(userName) и установить свойства профиля таким образом, но свойства set остаются пустыми, а вызов AccountProfile.CurrentUser(userName).Save() не помещает ничего в базу данных. Я также попытался указать, что пользователь действителен и зарегистрирован, вызывая Membership.ValidateUser, FormsAuthentication.SetAuthCookie и т.д., Но текущий пользователь по-прежнему является ничтожным или анонимным (в зависимости от состояния файлов cookie браузера).

РЕШЕННЫЙ (ИЗМЕНИТЬ ДАЛЬШЕ, ПОСМОТРЕТЬ НИЖЕ): Основываясь на объяснении Фрэнси Пенова и еще нескольких экспериментах, я понял эту проблему. Код Joel и измененные варианты будут работать только с существующим профилем. Если профиль отсутствует, ProfileBase.Create(userName) будет возвращать новый пустой объект при каждом его вызове; вы можете установить свойства, но они не будут "прилипать", потому что новый экземпляр возвращается каждый раз, когда вы обращаетесь к нему. Установка HttpContext.Current.User в новый GenericPrincipal даст вам объект User, но не объект Profile, а ProfileBase.Create(userName) и HttpContext.Current.Profile все равно укажут на новые пустые объекты.

Если вы хотите создать профиль для вновь созданного Пользователя в том же запросе, вам необходимо вызвать HttpContext.Current.Profile.Initialize(userName, true). Затем вы можете заполнить инициализированный профиль и сохранить его, и он будет доступен для будущих запросов по имени, поэтому код Joel будет работать. Я использую HttpContext.Current.Profile только внутри, когда мне нужно создать/получить доступ к профилю сразу после создания. По любым другим запросам я использую ProfileBase.Create(userName), и я раскрыл только эту версию как общедоступную.

Обратите внимание, что Franci прав: если вы хотите создать Пользователя (и Роли) и установить его как Проверенный в первом раунде, и попросите пользователя войти в систему, вы сможете получить доступ к профилю гораздо проще с помощью кода Джоэля на последующем запросе. Что меня бросило, так это то, что роли сразу доступны при создании пользователя без какой-либо инициализации, но профиль отсутствует.

Мой новый код учетной записи:

public static AccountProfile CurrentUser
{
    get
    {
        if (Membership.GetUser() != null)
            return ProfileBase.Create(Membership.GetUser().UserName) as AccountProfile;
        else
            return null;
    }
}

internal static AccountProfile NewUser
{
    get { return System.Web.HttpContext.Current.Profile as AccountProfile; }
}

Создание нового пользователя:

MembershipUser user = Membership.CreateUser(userName, password);
Roles.AddUserToRole(userName, "MyBasicUserRole");
AccountProfile.NewUser.Initialize(userName, true);
AccountProfile.NewUser.FullName = "Snoopy";
AccountProfile.NewUser.Save();

Последующий доступ:

if (Membership.ValidateUser(userName, password))
{
    string name = AccountProfile.CurrentUser.FullName;
}

Далее спасибо Франци за объяснение жизненного цикла аутентификации - я вызываю FormsAuthentication.SetAuthCookie в моей функции проверки, но я возвращаю bool для указания успеха, потому что User.Identity.IsAuthenticated не будет истинным, пока последующий запрос.

ПЕРЕСМОТРЕНЫ: Я идиот. Вышеприведенное объяснение работает в узком случае, но не решает основной проблемы: вызов CurrentUser каждый раз возвращает новый экземпляр объекта, независимо от того, является ли он существующим профилем или нет. Поскольку он был определен как свойство, я не думал об этом и писал:

AccountProfile.CurrentUser.FullName = "Snoopy";
AccountProfile.CurrentUser.OtherProperty = "ABC";
AccountProfile.CurrentUser.Save();

который (конечно) не работает. Это должно быть:

AccountProfile currentProfile = AccountProfile.CurrentUser;
currentProfile.FullName = "Snoopy";
currentProfile.OtherProperty = "ABC";
currentProfile.Save();

Это моя собственная ошибка, чтобы полностью игнорировать эту основную точку, но я думаю, что объявление CurrentUser как свойства означает, что это объект, с которым можно манипулировать. Вместо этого он должен быть объявлен как GetCurrentUser().

Ответ 1

Создание пользователя просто добавляет его в список пользователей. Однако это не подтверждает подлинность или авторизацию нового пользователя для текущего запроса. Вы также должны аутентифицировать пользователя в текущем контексте запроса или для последующих запросов.

Membership.ValidateUser будет только проверять учетные данные, но не аутентифицирует пользователя для текущих или последующих запросов. FormsAuthentication.SetAuthCookie установит билет проверки подлинности в потоке ответа, поэтому следующий запрос будет аутентифицирован, но он не повлияет на состояние текущего запроса.

Самый простой способ аутентификации пользователя - вызвать FormsAuthentication.RedirectFromLoginPage (при условии, что вы используете проверку подлинности в вашем приложении). Однако это фактически вызовет новый HTTP-запрос, который будет аутентифицировать пользователя.

В качестве альтернативы, если вам нужно продолжить свою логику для обработки текущего запроса, но хотите, чтобы пользователь был аутентифицирован, вы можете создать GenericPrincipal, назначить ему идентификатор нового пользователя и установить HttpContext.User на тот главный.

Ответ 2

У вас возникнут проблемы с этим подходом, если вы включите anonymousIdentification. Вместо User Members.GetUser(). UserName, я бы предложил использовать HttpContext.Profile.UserName.

Как это...

private UserProfile _profile;
private UserProfile Profile
{
    get { return _profile ?? (_profile = (UserProfile)ProfileBase.Create(HttpContext.Profile.UserName)); }
}

Совет шляп: SqlProfileProvider - можете ли вы использовать Profile.GetProfile() в проекте?

Ответ 3

Прежде всего, спасибо @Jeremy за то, что вы делитесь своими результатами. Вы помогли мне двигаться в правильном направлении. Во-вторых, извините за то, что столкнулся с этим старым сообщением. Надеюсь, это поможет кому-то подключить точки.

Как я, наконец, получил эту работу, было использование следующего статического метода внутри моего класса профиля:

internal static void InitializeNewMerchant(string username, Merchant merchant)
{
    var profile = System.Web.HttpContext.Current.Profile as MerchantProfile;
    profile.Initialize(username, true);
    profile.MerchantId = merchant.MerchantId;
    profile.Save();
}