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

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

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

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

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

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

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) и я выставил только эту версию как публичную.

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

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

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

Кроме того, спасибо Franci за объяснение жизненного цикла аутентификации - я вызываю 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(),

3 ответа

Решение

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

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

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

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

При таком подходе у вас возникнут проблемы, если вы включите 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() в проекте?

Прежде всего, спасибо @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();
}
Другие вопросы по тегам