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

Я реализовал пользовательский объект Profile в коде, как описано здесь Джоэлом:

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

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

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

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

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

AccountProfile.CurrentUser.FullName = "Snoopy";

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

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

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

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

Мой новый код AccountProfile:

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 в своей функции проверки, но возвращаю логическое значение, указывающее на успех, потому что 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().


person Jeremy Gruenwald    schedule 30.03.2010    source источник


Ответы (3)


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

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

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

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

person Franci Penov    schedule 30.03.2010
comment
Олицетворение обычно подразумевает проверку подлинности Windows, для которой требуется WindowsPrincipal. Олицетворение может быть выполнено с помощью токена безопасности для олицетворенного пользователя или с помощью учетных данных. Учитывая, что текущий контекст не аутентифицирован, скорее всего, нет способа получить токен безопасности для пользователя. Таким образом, единственный выбор - выдать себя за учетные данные. Можно было бы создать WindowsPrincipal с правильным идентификатором, вызвав LogonUser (при условии, что код знает учетные данные для пользователя Windows). Однако я этого не пробовал, поэтому не могу поручиться, что это обязательно сработает. - person Franci Penov; 30.03.2010
comment
Это невероятно полезное объяснение, спасибо. Создание GenericPrincipal (из моего FormsAuthenticationTicket) позволило мне установить HttpContext.Current.User. Однако я все еще не могу установить значения профиля. Если я использую ProfileBase.Create (Membership.GetUser (). UserName) .SetPropertyValue, ничего не происходит - он больше не генерирует исключение, но свойство остается пустым. Если я использую HttpContext.Current.Profile.SetPropertyValue, он говорит, что я не могу установить свойства в анонимном профиле, подразумевая, что это разные объекты профиля, которые мне каким-то образом нужно объединить. - person Jeremy Gruenwald; 30.03.2010

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

Нравится...

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

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

person Bill Mild    schedule 21.01.2012

Прежде всего, спасибо @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();
}
person Vinney Kelly    schedule 01.07.2011