Интерфейс Profiles API
91ASP.NET --- Безопасность в ASP.NET --- Интерфейс Profiles API
Хотя ваша страница автоматически получает информацию профиля для текущего пользователя, это не избавляет от извлечения и модификации профилей других пользователей. Фактически есть два инструмента, которые помогут решить эту задачу - классы ProfileBase и ProfileManager.
Объект ProfileBase (предоставленный свойством Page.Profile) включает удобную функцию GetProfile(), которая извлекает информацию профиля определенного пользователя по его имени. Ниже показан пример с аутентифицированным пользователем:
<form id="form1" runat="server">
<div>
Имя пользователя: <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="cmdGet" runat="server" Text="Получить профиль" OnClick="cmdGet_Click" />
<br /><br />
<asp:Label ID="Label1" runat="server"></asp:Label>
</div>
</form>
protected void cmdGet_Click(object sender, EventArgs e)
{
ProfileCommon profile = Profile.GetProfile(TextBox1.Text);
Label1.Text = "Этот пользователь живет в стране <b>"
+ profile.Address.Country + "</b>";
}
Метод GetProfile() возвращает объект ProfileCommon. Однако вы не найдете ProfileCommon в библиотеке классов .NET. Это связано с тем, что ProfileCommon - динамически генерируемый класс, который ASP.NET создает для хранения информации профиля веб-приложения. В этом примере профиль определяет свойство по имени Address, так что извлечь эту информацию можно с использованием свойства ProfileCommon.Address.
Обратите внимание, что как только получен объект ProfileCommon, с ним можно взаимодействовать точно так же, как с профилем текущего пользователя. Можно даже вносить изменения. Единственное отличие в том, что эти изменения не сохраняются автоматически. Чтобы сохранить изменения, понадобится вызвать метод Save() объекта ProfileCommon. Объект ProfileCommon также добавляет свойства LastActivityDate и LastUpdateDate, которые можно использовать для определения момента времени, когда к определенному профилю осуществлялось обращение или когда он последний раз был модифицирован.
Попытка извлечь несуществующий профиль не приводит к получению сообщения об ошибке. Вместо этого просто возвращаются пустые данные. Если изменить эти данные и сохранить профиль, будет создана новая запись профиля. Проверить это условие можно, заглянув в свойство ProfileCommon.LastUpdatedDate. Если профиль еще не был создан, значением этого свойства будет нулевая дата (другими словами, нулевой день нулевого месяца нулевого (0000) года).
Для решения других задач с профилями можно воспользоваться классом ProfileManager из пространства имен System.Web.Profile, который предлагает удобные статические методы, перечисленные в таблице ниже. Многие из этих методов работают с классом ProfileInfo, представляющим информацию о профиле. ProfileInfo включает имя пользователя (UserName), даты последнего обновления и последней активности (LastActivityDate и LastUpdateDate), размер профиля в байтах (Size), а также признак принадлежности анонимному пользователю (IsAnonymous). Он не предоставляет действительных значений самого профиля.
Метод | Описание |
---|---|
DeleteProfile() | Удаляет профиль указанного пользователя |
DeleteProfiles() | Принимает массив имен пользователей и удаляет сразу множество профилей |
DeleteInactiveProfiles() | Удаляет профили, которые не использовались с указанного времени. Потребуется также передать значение из перечисления AuthenticationOption, чтобы указать, профили какого типа необходимо удалить (All, Anonymous или Authenticated) |
GetNumberOfProfiles() | Возвращает количество записей с профилями в хранилище данных |
GetNumberOfInactiveProfiles() | Возвращает количество записей с профилями, которые не использовались с указанного времени |
GetAllInactiveProfiles() | Возвращает информацию профилей, которые не использовались с указанного времени. Профили возвращаются как объекты ProfileInfo |
GetAllProfiles() | Извлекает все данные профилей из источника данных как коллекцию объектов ProfileInfo. При этом можно выбирать, какого типа профили должны быть извлечены (All, Anonymous или Authenticated). Существует перегруженная версия этого метода, использующая разбиение на страницы и извлекающая только часть полного набора записей, указанную начальным индексом и размером страницы |
FindProfilesByUserName() | Извлекает коллекцию объектов ProfileInfo, которые соответствуют определенному имени пользователя. SqlProfileProvider использует конструкцию LIKE при попытке найти соответствующее имя пользователя. Это значит, что можно использовать шаблоны, подобные символу %. Например, поиск имени пользователя user% дает в результате такие значения, как user1, user2, user_guest и т.д. Существует перегруженная версия с разбиением на страницы |
FindInactiveProfilesByUserName() | Извлекает информацию о профилях, которые не использовались с указанного времени. Можно также отфильтровать определенные типы профилей (All, Anonymous или Authenticated) либо искать определенные имена пользователей (сравнивая с шаблоном). Возвращается коллекция объектов ProfileInfo |
Например, для удаления профиля текущего пользователя понадобится единственная строка кода:
ProfileManager.DeleteProfile(User.Identity.Name);
Если нужно отобразить полный список пользователей на веб-странице (не включая анонимных пользователей), следует добавить элемент управления GridView со свойством AutoGenerateColumns, установленным в true, и воспользоваться следующим кодом:
protected void Page_Load(object sender, EventArgs e)
{
GridView1.DataSource = ProfileManager.GetAllProfiles(
ProfileAuthenticationOption.Authenticated);
GridView1.DataBind();
}
Результат показан на рисунке ниже:
Анонимные профили
Все рассмотренные до сих пор примеры предполагали, что пользователь аутентифицирован до того, получена или сохранена любая информация о профилях. Обычно так оно и есть. Однако иногда бывает удобно создать временный профиль для нового, неизвестного пользователя. Например, большинство веб-сайтов электронной коммерции разрешают новому пользователю начинать добавление элементов в корзину для покупок перед началом регистрации. Если нужно обеспечить такое поведение, и решено сохранять корзину для покупок в профиле, понадобится какой-то способ уникальной идентификации анонимных пользователей.
В ASP.NET для этого предусмотрено средство анонимной идентификации. Основная идея состоит в том, что средство анонимной идентификации автоматически генерирует случайный идентификатор для любого анонимного пользователя. Этот случайный идентификатор сохраняет информацию профиля в базе данных, даже несмотря на недоступность пользовательского идентификатора. Пользовательский идентификатор отслеживается на стороне клиента с применением cookie-наборов (или же в URL-адресе, если выбран режим без cookie-наборов). Как только cookie-набор исчезает (например, когда анонимный пользователь закрывает и снова открывает браузер), старый анонимный сеанс теряется, и создается новый.
Анонимная идентификация чревата появлением множества отброшенных профилей, на которые расходуется пространство в базе данных. По этой причине анонимная идентификация по умолчанию отключена. Однако ее можно включить с помощью элемента <anonymousIdentification> в файле web.config, как показано ниже:
<configuration>
...
<system.web>
<anonymousIdentification enabled="true"/>
...
</system.web>
</configuration>
Кроме того, понадобится пометить каждое свойство профиля, которое будет сохраняться для анонимного пользователя, добавив атрибут allowAnonymous и установив его в true. Это позволит сохранять только некоторую базовую информацию и ограничить использование крупных объектов в профилях анонимных пользователей:
<properties>
<add name="Address" type="Address" allowAnonymous="true"/>
</properties>
Если используется сложный тип, то атрибут allowAnonymous представляет собой настройку типа "все или ничего". Весь объект конфигурируется для того, чтобы либо поддерживать анонимное хранение, либо нет.
Элемент <anonymousIdentification> также поддерживает множество необязательных атрибутов, которые позволяют устанавливать имя cookie-набора и таймаут, указывать, что cookie-наборы должны использоваться только по соединению SSL, контролировать их безопасность (проверка достоверности и шифрование), предотвращая хищения и подделку, а также конфигурировать отслеживание их идентификаторов.
За более подробной информацией обращайтесь к описанию настроек конфигурации дня аутентификации форм и управления ролями, которые используют те же настройки.
Если используется анонимная идентификация, то хорошей идеей будет регулярное удаление старых анонимных сеансов с помощью хранимой процедуры aspnet_Profile_DeleteInactiveProfiles, которую можно запускать по расписанию с помощью SQL Server Agent. Старые профили также можно удалять, применяя для этого класс ProfileManager, как было описано в предыдущей статье.
Миграция анонимных профилей
Проблема, которая возникает при использовании анонимных профилей, связана с тем, что необходимо делать с информацией профиля, когда регистрируется пользователь, ранее бывший анонимным. Например, на веб-сайте электронной коммерции пользователь может выбрать несколько покупок и затем зарегистрироваться, чтобы завершить транзакцию. В этот момент понадобится обеспечить, чтобы информация о его корзине для покупок из анонимного профиля была перенесена в профиль соответствующего зарегистрированного пользователя.
К счастью, ASP.NET предлагает решение - воспользоваться событием ProfileModule.MigrateAnonymous. Это событие (которое может быть обработано в файле global.asax) инициируется всякий раз, когда становится доступным анонимный идентификатор (через cookie-набор или URL-адрес, если выбран режим без cookie-наборов) и текущий пользователь аутентифицируется.
Базовая техника обработки события MigrateAnonymous предполагает загрузку профиля для анонимного пользователя вызовом Profile.GetProfile() с передачей анонимного идентификатора, который предоставляется обработчику события через ProfileMigrateEventArgs.
Загрузив эти данные, сразу можно перенести все установки в новый профиль вручную. Можно выбрать лишь несколько настроек для переноса в новый профиль, а также выполнить любую необходимую дополнительную обработку. И, наконец, код должен удалить информацию анонимного профиля из базы данных и очистить анонимный идентификатор, чтобы событие MigrateAnonymous не было инициировано снова:
void Profile_MigrateAnonymous(Object sender, ProfileMigrateEventArgs e)
{
// Получить анонимный профиль
ProfileCommon anonProfile = Profile.GetProfile(e.AnonymousID);
// Скопировать информацию в аутентифицированный профиль
// (но только, если она есть)
if (anonProfile.Address.Name != null || anonProfile.Address.Name != "")
{
Profile.Address = anonProfile.Address;
}
// Удалить анонимный профиль из базы данных.
// (Этот шаг можно пропустить, чтобы повысить производительность, если на
// сервере базы работает автоматическая процедура удаления старых профилей.)
System.Web.Profile.ProfileManager.DeleteProfile(e.AnonymousID);
// Удалить анонимный идентификатор
AnonymousIdentificationModule.ClearAnonymousIdentifier();
}
Эту задачу следует выполнять осторожно. Если анонимная идентификация разрешена, то при каждом входе пользователя инициируется событие MigrateAnonymous, даже если пользователь еще не ввел никакой информации в свой анонимный профиль. Это проблема, потому что можно случайно перезаписать реальный (сохраненный) профиль пользователя пустым анонимным профилем. Проблема еще более усугубляется тем, что сложные типы (вроде объекта Address) создаются автоматически средствами ProfileModule, так что даже не получится проверить null-ссылку для определения того, имеет ли пользователь анонимную информацию об адресе.
В предыдущем примере код проверяет отсутствие свойства Name в объекте Address. Если эта информация не является частью анонимного профиля, то никакая информация не переносится. В более сложном примере можно было бы проверять индивидуальные свойства по отдельности либо перемещать анонимный профиль, только если информация в профиле зарегистрированного пользователя пропущена или устарела.