Службы приложений ASP.NET AJAX

104

Создание и вызов специальных веб-служб - ценная технология ASP.NET AJAX. Веб-службы можно использовать для возврата дополнительной информации из серверной базы данных (как в примере TerritoriesService предыдущей статьи), запуска серверной задачи, получения специфичной для пользователя информацию сеанса и т.д. Чтобы облегчить задачу разработчикам, ASP.NET AJAX включает в себя также встроенные службы, которые можно применять для доступа к трем часто используемым средствам: аутентификация с помощью форм, членство и роли, а также профили пользователей. Хотя аналогичные службы можно создать самостоятельно, целесообразнее, чтобы платформа ASP.NET AJAX предоставила целостную модель веб-служб. Вполне вероятно, что в будущем ASP.NET AJAX станет предлагать больше встроенных служб.

Прежде чем можно будет использовать любую из этих служб, понадобится добавить раздел <system.web.extensions> в файл web.config. Внутри него для каждой службы, которую желательно использовать, должен быть предусмотрен элемент:

<configuration>
  <system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService ... />
        <profileService ... />
        <roleService ... />
      </webServices>
    </scripting>
  </system.web.extensions>
</configuration>

При использовании служб в клиентском коде JavaScript их вызывают через свойство Sys.Services. Например, Sys.Services.AuthenticationService предоставляет доступ к методам службы аутентификации.

В следующих разделах показано, как активизировать и использовать каждую из этих трех служб.

Служба аутентификации

Служба аутентификации позволяет использовать аутентификацию с помощью форм за счет вызова веб-службы. Для ее активизации понадобится добавить элемент <authenticationService> в файл web.config. Если свойство requireSSL установлено в true, cookie-набор передается обратно серверу один раз, когда браузер запрашивает страницу через SSL-соединение. В противном случае cookie-набор отправляется с каждым запросом.

Одно из ограничений службы аутентификации состоит в том, что она поддерживает только аутентификацию на основе cookie-наборов.

При использовании службы аутентификации должна также применяться аутентификация с помощью форм. Ниже приведено содержимое файла web.config, который предоставляет эти сведения:

<configuration>
  <system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService enabled="true" requireSSL="true" />
      </webServices>
    </scripting>
  </system.web.extensions>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/MyLogin.aspx" />
    </authentication>
    <membership defaultProvider="MyMembershipProvider">
      <providers>
        <add name="MyMembershipProvider"
             connectionStringName="aspnetdb"
             type="System.Web.Security.SqlMembershipProvider"
             passwordFormat="Hashed" />
      </providers>
    </membership>
  </system.web>
  <appSettings>
    <add key="ValidationSettings:UnobtrusiveValidationMode" value="None"/>
  </appSettings>
  <connectionStrings>
    <add name="aspnetdb" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=aspnetdb;Integrated Security=True"/>
  </connectionStrings>
</configuration>

Как только служба аутентификации активизирована, ее члены, описанные в таблице ниже, можно использовать в клиентском коде JavaScript:

Методы класса AuthenticationService
Клиентский метод Описание
login()

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

logout()

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

get_isLoggedIn()

Возвращает значение true, если в настоящий момент пользователь зарегистрирован, и значение false - в противном случае

Может возникать вопрос, действительно ли полезен асинхронный метод login(). В конце концов, пользователь обычно должен завершить процесс регистрации, прежде чем приступить к следующей задаче. Поэтому мало что можно сделать, пока асинхронный запрос регистрации идет полным ходом. Одно незначительное преимущество login() состоит в том, что он позволяет пользователям входить в систему, не обновляя страницу (Это особенно важно, поскольку серверный элемент управления Login не поддерживается элементом управления UpdatePanel, о котором будет рассказано позже.)

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

В следующем примере используется последний подход с вызовом функции onLoginCompleted() после того, как сервер ответил, и функции onLoginFailed, чтобы сообщить пользователю о неудачном исходе асинхронного вызова:

function doLogin()
{
        // Извлечь имя пользователя и пароль из двух текстовых полей
        var username = $get("txtUserName");
        var password = $get("txtPassword");
        
        // Войти с использованием службы аутентификации
        Sys.Services.AuthenticationService.login(username.value, password.value,
          false, null, null, onLoginCompleted, onLoginFailed, null);
}

Последний аргумент метода login() принимает любой объект. Его называют объектом контекста пользователя, и эта модель сохраняется во всех асинхронных вызовах службы приложении AJAX ASP.NET. По сути, объект контекста пользователя передается обратным вызовам. Функции обратного вызова регистрации выглядят примерно так:

function onLoginCompleted(validCredentials, userContext, methodName)
{
        // Попытка асинхронного входа завершена, но все же нужно проверить булевский 
        // параметр validCredentials, чтобы определить, была ли она успешной
        if (validCredentials == false)
        {
            $get("lblStatus").innerHTML = "Вход не выполнен";
        }
        else
        {
            $get("lblStatus").innerHTML = "Вы вошли в систему";
        }
}

function onLoginFailed(error, userContext, methodName)
{
        alert(error.get_message());
}

Обратите внимание, что завершение процесса входа свидетельствует всего лишь о получении ответа сервера. Оно не указывает, что пользователь был успешно зарегистрирован. Чтобы определить, вошел ли пользователь, понадобится проверить параметр validCredentials, как было показано в примере.

Служба ролей

Служба ролей позволяет использовать авторизацию на основе ролей, вызывая соответствующую веб-службу. Для ее активизации применяется элемент <roleService> в файле web.config. Чтобы использовать службу ролей, пользователь должен уже быть аутентифицирован (через аутентификацию с помощью форм или аутентификацию Windows). Хотя это и не обязательно выполнять посредством службы аутентификации с помощью форм, во многих случаях требуется одновременная реализация и службы аутентификации, и службы ролей.

Ниже приведено содержимое файла web.config, в котором применяется аутентификация с помощью форм (посредством службы аутентификации) и роли (через службу ролей):

<configuration>
  <system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService enabled="true" requireSSL="true" />
        <roleService enabled="true" />
      </webServices>
    </scripting>
  </system.web.extensions>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/MyLogin.aspx" />
    </authentication>
    <roleManager enabled="true" />
    ...
  </system.web>
  ...
</configuration>

Как только служба ролей активизирована, члены класса Sys.Services.RoleService, описанные в таблице ниже, можно использовать в клиентском коде JavaScript:

Методы класса RoleService
Клиентский метод Описание
load()

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

isUserInRole()

Возвращает значение true, если пользователю была назначена указанная роль. Перед использованием этого метода должен быть вызван метод load()

get_roles()

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

Первый шаг по использованию службы ролей предусматривает вызов метода load() и предоставление соответствующих обратных вызовов. После запуска завершенного обратного вызова можно проверить принадлежность пользователя к ролям.

В следующем коде демонстрируется наиболее распространенный способ реализации этой модели. Код отображает конкретный элемент <div> (по имени adminControls), когда текущий пользователь является администратором:

function pageLoad()
{
        // Страница загружается. Получить роли текущего пользователя
        Sys.Services.RoleService.load(onLoadRolesCompleted, onLoadRolesFailed, null);
}
    
function onLoadRolesCompleted(result, userContext, methodName)
{
        // Роли получены.
        // Проверить принадлежность к роли и сконфигурировать страницу
        if (Sys.Services.RoleService.isUserInRole("Administrator"))
        {
            $get("adminControls").style.display = "block";
        }
}
    
function onLoadRolesFailed(error, userContext, methodName)
{
        alert(error.get_message());
}

После того, как страница загружена, а клиентская платформа ASP.NET AJAX инициализирована, ASP.NET AJAX автоматически вызывает функцию pageLoad() (если она была добавлена). Это похоже на обработку JavaScript-события window.onload.

Помните, что RoleService.load() следует вызывать, только если известно, что текущий пользователь аутентифицирован. Это означает, что load() можно безопасно вызывать на защищенной (неанонимной) странице или после вызова Sys.Services.AuthenticationService.get_isLoggedIn() и проверки, что возвращенное значение равно true. Если вызвать метод load() в ситуации, когда пользователь не аутентифицирован, операция будет казаться завершенной успешно, но никакая информация о ролях при этом не возвращается.

Поддержание безопасности при использовании служб приложений

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

Наиболее важно усвоить следующее правило: никогда не полагайтесь на клиентский код в деле сокрытия критичной информации. В предыдущем примере элемент <div> выводится на экран или скрывается в зависимости от результата проверки роли. Однако содержимое этого элемента легко извлечь из клиентского кода разметки страницы, даже если в настоящий момент он не отображается на экране. По этой причине данный элемент <div> не является приемлемым местом для размещения чувствительного содержимого (такого как информация, предназначенная только для администраторов). Он подходит для размещения элементов управления, которые предназначены только для администраторов (например, ссылок, которые для других типов пользователей не работают, вызывая исключение безопасности), при условии, что они косвенно не предоставляют информацию, которая может оказаться полезной злоумышленникам.

И, наконец, если вы предоставляете вызываемый клиентом веб-метод, следует исходить из предположения, что он может быть вызван пользователями в любой роли (и даже анонимными пользователями, если он находится в незащищенной части веб-сайта). Клиентский код мог бы проверять принадлежность к ролям, прежде чем разрешать вызов определенного веб-метода, но злоумышленники могут вызвать этот же метод посредством других механизмов. Таким образом, защищенный веб-метод должен выполнять проверку собственной роли.

Служба профилей

Служба профилей дает возможность использовать средство профилей для получения информации о пользователе, которую ASP.NET автоматически сохраняет в серверной базе данных. Для активизации службы профилей применяется элемент <profileService> в файле web.config. При этом понадобится предоставить список разделенных запятой значений свойств, которые будут сделаны доступными (в атрибуте readAccessProperties) и изменяемыми (в атрибуте writeAccessProperties) на стороне клиента. Помните, что каждый выбор оказывает определенное влияние на безопасность.

При предоставлении доступа по чтению зарегистрированные пользователи в любое время могут взломать систему, чтобы прочитать эти свойства в своем профиле. При предоставлении доступа по записи зарегистрированные пользователи могут обойти клиентский код, чтобы произвольно изменить эти значения в своих профилях, избегая любой проверки достоверности или проверки данных. Ни одна из этих возможностей не является обязательным поводом для тревоги, но, в зависимости от типа информации, хранимой в профиле, и способа ее обработки, это может приводить к проблеме в некоторых ситуациях. Осторожность и подробный анализ угроз - всегда целесообразна, если вы планируете применять эти службы в веб-сайте, к которому предъявляются высокие требования относительно безопасности.

Ниже приведено содержимое файла web.config, в котором используется служба профилей (и служба аутентификации с помощью форм для регистрации пользователя):

<configuration>
  <system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService enabled="true" requireSSL="true" />
        <profileService readAccessProperties="FirstName, LastName, CustomCode"
                        writeAccessProperties="FirstName, LastName"/>
      </webServices>
    </scripting>
  </system.web.extensions>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="~/MyLogin.aspx" />
    </authentication>
    <profile enabled="true">
      <properties>
        <add name="FirstName" />
        <add name="LastName" />
        <add name="CustomCode"/>
      </properties>
    </profile>
    ...
  </system.web>
  ...
</configuration>

Как только служба профилей активизирована, элементы класса Sys.Services.ProfileService, описанные в таблице ниже, можно использовать в клиентском коде JavaScript:

Методы класса ProfileService
Клиентские методы и свойства Описание
load()

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

properties

Предоставляет коллекцию данных профиля, которые можно читать или изменять. Доступ к индивидуальным свойствам осуществляется по имени: Sys.ProfileService.properties.FirstName

save()

Передает текущие значения в коллекции свойств обратно веб-серверу, где они будут обновлены. Значения, которые не перечислены в атрибуте writeAccessProperties, серверу не передаются обратно, а просто игнорируются

Ниже приведен небольшой фрагмент клиентского кода JavaScript, который использует службу профилей для отображения данных свойства в окне предупреждения:

function pageLoad()
{
        Sys.Services.ProfileService.load(null, onLoadCompleted, onLoadFailed, null);
}

function onLoadCompleted(numProperties, userContext, methodName)
{
        var profile = Sys.Services.ProfileService.properties;
        alert ("Вас зовут " + profile.FirstName + " " + profile.LastName);
}

function onLoadFailed(error, userContext, methodName)
{
        alert(error.get_message());
}

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

Sys.Services.ProfileService.save(null, onSaveCompleted, onSaveFailed, null);

Это позволяет получать и изменять данные профиля, не инициируя полностраничной обратной отправки.

Пройди тесты
Лучший чат для C# программистов