Как воспользоваться преимуществами интегрированного конвейера IIS 7.0
IIS 6.0 и предыдущие версии позволяли разрабатывать компоненты приложения .NET через платформу ASP.NET. ASP.NET интегрирована со службами IIS через расширение ISAPI и предоставляет собственную модель обработки приложений и запросов. Это фактически предоставило два отдельных серверных конвейера: один для собственных фильтров ISAPI и компонентов расширения, а другой для компонентов управляемых приложений. ASP.NET компоненты будут выполняться полностью внутри пузырька расширения ISAPI ASP.NET и только для запросов, сопоставленных с ASP.NET в конфигурации сопоставления сценариев IIS.
IIS 7.0 и более поздних версий интегрирует среду выполнения ASP.NET с основным веб-сервером, предоставляя единый конвейер обработки запросов, который предоставляется как собственным, так и управляемым компонентам, называемым модулями. Ниже перечислены преимущества интеграции.
- Разрешение службам, предоставляемым как собственными, так и управляемыми модулями, применяться ко всем запросам, независимо от обработчика. Например, управляемую проверку подлинности с помощью форм можно использовать для всего содержимого, включая ASP-страницы, CGIS и статические файлы.
- Предоставление ASP.NET компонентов возможности, которые ранее были недоступны из-за их размещения в серверном конвейере. Например, управляемый модуль, предоставляющий функции перезаписи запросов, может перезаписать запрос до любой обработки сервера, включая проверку подлинности.
- Единое место для реализации, настройки, мониторинга и поддержки функций сервера, таких как конфигурация сопоставления с одним модулем и обработчиком, одна настраиваемая конфигурация ошибок, конфигурация авторизации с одним URL-адресом.
В этой статье рассматривается, как приложения ASP.NET могут использовать преимущества интегрированного режима в IIS 7.0 и более поздних версий, а также показаны следующие задачи:
- Включение и отключение модулей на уровне приложения.
- Добавление модулей управляемых приложений на сервер и их применение ко всем типам запросов.
- Добавление управляемых обработчиков.
Дополнительные сведения о создании модулей IIS 7.0 и более поздних версий см. в статье Разработка модулей и обработчиков IIS 7.0 и более поздних версий с помощью платформа .NET Framework.
Дополнительные советы по использованию интегрированного режима и разработке модулей IIS, использующих интеграцию ASP.NET в IIS 7.0 и более поздних версий, http://www.mvolo.com/см. в блоге . Там вы можете скачать несколько таких модулей, включая Перенаправление запросов к приложению с помощью модуля HttpRedirection, просмотр списков каталогов для веб-сайта IIS с помощью DirectoryListingModule и Отображение значков красивых файлов в приложениях ASP.NET с помощью IconHandler.
Предварительные требования
Чтобы выполнить действия, описанные в этом документе, необходимо установить следующие компоненты IIS 7.0 и более поздних версий.
ASP.NET
Установите ASP.NET с помощью панель управления Windows Vista. Выберите "Программы и компоненты" — "Включить или отключить компоненты Windows". Затем откройте "Службы IIS" — "Веб-службы" - "Функции разработки приложений" и проверка "ASP.NET".
Если у вас есть сборка Windows Server® 2008, откройте "диспетчер сервера" — "Роли" и выберите "Веб-сервер (IIS)". Щелкните "Добавить службы ролей". В разделе "Разработка приложений" проверка "ASP.NET".
Классический ASP
Мы хотим показать, как модули ASP.NET теперь работают со всем содержимым, а не только со страницами ASP.NET, поэтому установите классический ASP с помощью панель управления Windows Vista. Выберите "Программы" — "Включить или отключить компоненты Windows". Затем откройте "Службы IIS" — "Веб-службы" - "Функции разработки приложений" и проверка "ASP".
Если у вас есть сборка Windows Server 2008, откройте "диспетчер сервера" — "Роли" и выберите "Веб-сервер (IIS)". Щелкните "Добавить службы ролей". В разделе "Разработка приложений" проверка "ASP".
Добавление проверки подлинности с помощью форм в приложение
В рамках этой задачи мы включаем для приложения ASP.NET проверку подлинности на основе форм. В следующей задаче мы включим модуль Проверки подлинности с помощью форм для выполнения всех запросов к приложению, независимо от типа контента.
Сначала настройте проверку подлинности с помощью форм так же, как для обычного приложения ASP.NET.
Создание примера страницы
Чтобы проиллюстрировать эту функцию, мы добавим страницу default.aspx в корневой каталог веб-сайта. Откройте Блокнот (чтобы убедиться, что у вас есть доступ к каталогу wwwroot ниже, необходимо запустить от имени администратора. Щелкните правой кнопкой мыши значок Программы\Стандартные\Блокнот и выберите команду "Запуск от имени администратора") и создайте следующий файл: %systemdrive%\inetpub\wwwroot\default.aspx
. Вставьте в него следующие строки:
<%=Datetime.Now%>
<BR>
Login Name: <asp:LoginName runat="server"/>
Все, что делает default.aspx, — это текущее время и имя пользователя, выполнившего вход. Мы используем эту страницу позже, чтобы показать проверку подлинности с помощью форм в действии.
Настройка правил проверки подлинности на основе форм и контроль доступа
Теперь, чтобы защитить default.aspx с помощью проверки подлинности с помощью форм. Создайте файл web.config в каталоге %systemdrive%\inetpub\wwwroot
и добавьте конфигурацию, показанную ниже:
<configuration>
<system.web>
<!--membership provider entry goes here-->
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
<authentication mode="Forms"/>
</system.web>
</configuration>
Эта конфигурация задает ASP.NET режим проверки подлинности для использования проверки подлинности на основе форм и добавляет параметры авторизации для управления доступом к приложению. Эти параметры запрещают доступ анонимным пользователям (?) и разрешают только прошедших проверку подлинности пользователей (*).
Создание поставщика членства
Шаг 1. Необходимо предоставить хранилище проверки подлинности, в котором будут проверяться учетные данные пользователя. Чтобы проиллюстрировать глубокую интеграцию между ASP.NET и IIS 7.0 и более поздними версиями, мы используем собственный поставщик членства на основе XML (вы также можете использовать поставщик членства по умолчанию SQL Server, если установлен SQL Server).
Добавьте следующую запись сразу после элемента начальной <конфигурации configuration>/<system.web> в файле web.config:
<membership defaultProvider="AspNetReadOnlyXmlMembershipProvider">
<providers>
<add name="AspNetReadOnlyXmlMembershipProvider" type="AspNetReadOnlyXmlMembershipProvider" description="Read-only XML membership provider" xmlFileName="~/App_Data/MembershipUsers.xml"/>
</providers>
</membership>
Шаг 2. После добавления записи конфигурации необходимо сохранить код поставщика членства, предоставленный в приложении, как XmlMembershipProvider.cs в каталоге%systemdrive%\inetpub\wwwroot\App_Code
. Если этот каталог не существует, его необходимо создать.
Примечание
Если вы используете Блокнот, установите флажок Сохранить как все файлы, чтобы предотвратить сохранение файла как XmlMembershipProvider.cs.txt.
Шаг 3. Осталось только фактическое хранилище учетных данных. Сохраните приведенный ниже xml-фрагмент в виде файла MembershipUsers.xml в каталоге %systemdrive%\inetpub\wwwroot\App_Data
.
Примечание
Если вы используете Блокнот, установите флажок Сохранить как все файлы, чтобы предотвратить сохранение файла как MembershipUsers.xml.txt.
<Users>
<User>
<UserName>Bob</UserName>
<Password>contoso!</Password>
<Email>bob@contoso.com</Email>
</User>
<User>
<UserName>Alice</UserName>
<Password>contoso!</Password>
<Email>alice@contoso.com</Email>
</User>
</Users>
Если каталог App_Data не существует, его необходимо создать.
Примечание
Из-за изменений в системе безопасности в Windows Server 2003 и Windows Vista с пакетом обновления 1 (SP1) вы больше не можете использовать средство администрирования IIS для создания учетных записей пользователей членства, не являющихся общедоступными.
После выполнения этой задачи перейдите к средству администрирования IIS и добавьте или удалите пользователей для приложения. Запустите "INETMGR" из раздела "Выполнить..." Меню. Откройте знаки "+" в представлении в виде дерева слева, пока не появится элемент "Веб-сайт по умолчанию". Выберите "Веб-сайт по умолчанию", а затем перейдите вправо и выберите категорию "Безопасность". В остальных функциях отображаются пользователи .NET. Щелкните .NET Users (Пользователи ).NET) и добавьте одну или несколько учетных записей пользователей по своему выбору.
Просмотрите MembershipUsers.xml, чтобы найти только что созданных пользователей.
Создание страницы входа
Чтобы использовать проверку подлинности с помощью форм, необходимо создать страницу входа. Откройте Блокнот (чтобы убедиться, что у вас есть доступ к каталогу wwwroot ниже, необходимо запустить от имени администратора, щелкнув правой кнопкой мыши значок Программы\Стандартные\Блокнот и выбрав команду "Запуск от имени администратора") и создав файл login.aspx в каталоге %systemdrive%\inetpub\wwwroot
. Примечание. Не забудьте установить параметр Сохранить как: все файлы, чтобы предотвратить сохранение файла как login.aspx.txt. Вставьте в него следующие строки:
<%@ Page language="c#" %>
<form id="Form1" runat="server">
<asp:LoginStatus runat="server" />
<asp:Login runat="server" />
</form>
Это страница входа, на которую вы перенаправляетесь, когда правила авторизации запрещают доступ к определенному ресурсу.
Тестирование
Откройте окно интернет-Обозреватель и запросите http://localhost/default.aspx
. Вы увидите, что вы перенаправляетесь на login.aspx, так как изначально ваша проверка подлинности не выполнялась, и ранее мы отключили доступ к пользователям, не прошедшим проверку подлинности. При успешном входе с помощью одной из пар имени пользователя и пароля, указанных в MembershipUsers.xml, вы будете перенаправлены обратно на страницу default.aspx, запрошенную изначально. Затем на этой странице отображается текущее время и удостоверение пользователя, с помощью которого вы прошли проверку подлинности.
На этом этапе мы успешно развернули пользовательское решение для проверки подлинности с использованием проверки подлинности с помощью форм, элементов управления для входа и членства. Эта функция не является новой в IIS 7.0 или более поздней версии— она была доступна с ASP.NET 2.0 в предыдущих выпусках IIS.
Однако проблема заключается в том, что защищается только содержимое, обрабатываемые ASP.NET.
Если закрыть и повторно открыть окно браузера и запросить http://localhost/iisstart.htm
, учетные данные не запрашиваются. ASP.NET не участвует в запросе статического файла, например iisstart.htm. Поэтому он не может защитить его с помощью проверки подлинности на основе форм. Такое же поведение наблюдается с классическими ASP-страницами, программами CGI, скриптами PHP или Perl. Проверка подлинности с помощью форм является ASP.NET функцией и просто недоступна во время запросов к этим ресурсам.
Включение проверки подлинности с помощью форм для всего приложения
В этой задаче мы устраним ограничение ASP.NET в предыдущих выпусках и включим функцию проверки подлинности ASP.NET форм и авторизации url-адресов для всего приложения.
Чтобы воспользоваться преимуществами интеграции ASP.NET, приложение должно быть настроено для работы в режиме интеграции. Режим интеграции ASP.NET настраивается для каждого пула приложений, что позволяет размещать ASP.NET приложения в разных режимах параллельно на одном сервере. Пул приложений по умолчанию, в котором находится наше приложение, по умолчанию уже использует интегрированный режим, поэтому нам не нужно ничего делать.
Итак, почему мы не смогли испытать преимущества интегрированного режима, когда мы пытались получить доступ к статической странице ранее? Ответ заключается в параметрах по умолчанию для всех модулей ASP.NET, поставляемых с IIS 7.0 и более поздних версий.
Использование преимуществ интегрированного конвейера
Конфигурация по умолчанию для всех управляемых модулей, поставляемых с IIS 7.0 и более поздних версий, включая модули проверки подлинности на основе форм и авторизации URL-адресов, использует предварительное условие, чтобы эти модули применялись только к содержимому, которым управляет обработчик (ASP.NET). Это делается из соображений обратной совместимости.
Удаляя предварительное условие, мы выполняем нужный управляемый модуль для всех запросов к приложению независимо от содержимого. Это необходимо для защиты статических файлов и любого другого содержимого приложения с помощью проверки подлинности на основе форм.
Для этого откройте файл web.config приложения, расположенный в каталоге %systemdrive%\inetpub\wwwroot
, и вставьте следующие строки сразу под первым <элементом конфигурации> :
<system.webServer>
<modules>
<remove name="FormsAuthenticationModule" />
<add name="FormsAuthenticationModule" type="System.Web.Security.FormsAuthenticationModule" />
<remove name="UrlAuthorization" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<remove name="DefaultAuthentication" />
<add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
</modules>
</system.webServer>
Эта конфигурация повторно добавляет элементы модуля без предварительного условия, что позволяет выполнять их для всех запросов к приложению.
Тестирование
Закройте все экземпляры интернет-Обозреватель, чтобы введенные ранее учетные данные больше не кэшировались. Откройте интернет-Обозреватель и отправьте запрос к приложению по следующему URL-адресу:
http://localhost/iisstart.htm
Вы будете перенаправлены на страницу login.aspx, чтобы войти в систему.
Войдите с помощью пары имени пользователя и пароля, которая использовалась ранее. После успешного входа вы будете перенаправлены обратно к исходному ресурсу, на котором отображается страница приветствия IIS.
Примечание
Несмотря на запрос статического файла, модули проверки подлинности на управляемых формах и модуль авторизации URL-адресов предоставили свои службы для защиты ресурса.
Чтобы еще больше проиллюстрировать это, мы добавим классическую страницу ASP и защитим ее с помощью проверки подлинности с помощью форм.
Откройте Блокнот (чтобы убедиться, что у вас есть доступ к каталогу wwwroot ниже, необходимо запустить от имени администратора. Щелкните правой кнопкой мыши значок Программы\Стандартные\Блокнот и выберите команду "Запуск от имени администратора") и создайте файл page.asp в каталоге %systemdrive%\inetpub\wwwroot
.
Примечание
Если вы используете Блокнот, установите флажок Сохранить как: Все файлы, чтобы предотвратить сохранение файла как page.asp.txt. Вставьте в него строки ниже:
<%
for each s in Request.ServerVariables
Response.Write s & ": "&Request.ServerVariables(s) & VbCrLf
next
%>
Закройте все экземпляры интернет-Обозреватель еще раз. В противном случае учетные данные по-прежнему кэшируются и запрашиваются http://localhost/page.asp
. Вы снова будете перенаправлены на страницу входа и после успешной проверки подлинности отобразите страницу ASP.
Поздравляем! Вы успешно добавили управляемые службы на сервер, включив их для всех запросов к серверу независимо от обработчика!
Итоги
В этом пошаговом руководстве показано, как можно использовать интегрированный режим ASP.NET, чтобы сделать функциональные возможности ASP.NET доступными не только для ASP.NET страниц, но и для всего приложения.
Что еще более важно, теперь вы можете создавать новые управляемые модули с помощью знакомых API ASP.NET 2.0, которые могут выполняться для всего содержимого приложения и предоставляют расширенный набор служб обработки запросов для вашего приложения.
Вы можете проверка блоге , https://www.mvolo.com/чтобы получить дополнительные советы по использованию интегрированных режимов и разработке модулей IIS, использующих интеграцию ASP.NET в IIS 7 и более поздних версий. Там также можно скачать ряд таких модулей, включая перенаправление запросов к приложению с помощью модуля HttpRedirection, списки каталогов с хорошим просмотром для веб-сайта IIS с помощью DirectoryListingModule и отображение значков красивых файлов в приложениях ASP.NET с помощью IconHandler.
Приложение
Этот поставщик членства основан на примере поставщика членства XML, найденного в этом разделе Поставщики членства.
Чтобы использовать этот поставщик членства, сохраните код как XmlMembershipProvider.cs в каталоге %systemdrive%\inetpub\wwwroot\App\_Code
. Если этот каталог не существует, его необходимо создать. Примечание. Не забудьте установить параметр Сохранить как: Все файлы при использовании Блокнота, чтобы предотвратить сохранение файла как XmlMembershipProvider.cs.txt.
Примечание
Этот пример поставщика членства предназначен только для целей этой демонстрации. Он не соответствует рекомендациям и требованиям безопасности для поставщика членства в рабочей среде, включая безопасное хранение паролей и аудит действий пользователей. Не используйте этот поставщик членства в приложении!
using System;
using System.Xml;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Web.Security;
using System.Web.Hosting;
using System.Web.Management;
using System.Security.Permissions;
using System.Web;
public class AspNetReadOnlyXmlMembershipProvider : MembershipProvider
{
private Dictionary<string, MembershipUser> _Users;
private string _XmlFileName;
// MembershipProvider Properties
public override string ApplicationName
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
public override bool EnablePasswordRetrieval
{
get { return false; }
}
public override bool EnablePasswordReset
{
get { return false; }
}
public override int MaxInvalidPasswordAttempts
{
get { throw new NotSupportedException(); }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { throw new NotSupportedException(); }
}
public override int MinRequiredPasswordLength
{
get { throw new NotSupportedException(); }
}
public override int PasswordAttemptWindow
{
get { throw new NotSupportedException(); }
}
public override MembershipPasswordFormat PasswordFormat
{
get { throw new NotSupportedException(); }
}
public override string PasswordStrengthRegularExpression
{
get { throw new NotSupportedException(); }
}
public override bool RequiresQuestionAndAnswer
{
get { return false; }
}
public override bool RequiresUniqueEmail
{
get { throw new NotSupportedException(); }
}
// MembershipProvider Methods
public override void Initialize(string name,
NameValueCollection config)
{
// Verify that config isn't null
if (config == null)
throw new ArgumentNullException("config");
// Assign the provider a default name if it doesn't have one
if (String.IsNullOrEmpty(name))
name = "ReadOnlyXmlMembershipProvider";
// Add a default "description" attribute to config if the
// attribute doesn't exist or is empty
if (string.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description",
"Read-only XML membership provider");
}
// Call the base class's Initialize method
base.Initialize(name, config);
// Initialize _XmlFileName and make sure the path
// is app-relative
string path = config["xmlFileName"];
if (String.IsNullOrEmpty(path))
path = "~/App_Data/MembershipUsers.xml";
if (!VirtualPathUtility.IsAppRelative(path))
throw new ArgumentException
("xmlFileName must be app-relative");
string fullyQualifiedPath = VirtualPathUtility.Combine
(VirtualPathUtility.AppendTrailingSlash
(HttpRuntime.AppDomainAppVirtualPath), path);
_XmlFileName = HostingEnvironment.MapPath(fullyQualifiedPath);
config.Remove("xmlFileName");
// Make sure we have permission to read the XML data source and
// throw an exception if we don't
FileIOPermission permission =
new FileIOPermission(FileIOPermissionAccess.Read,
_XmlFileName);
permission.Demand();
// Throw an exception if unrecognized attributes remain
if (config.Count > 0)
{
string attr = config.GetKey(0);
if (!String.IsNullOrEmpty(attr))
throw new ProviderException
("Unrecognized attribute: " + attr);
}
}
public override bool ValidateUser(string username, string password)
{
// Validate input parameters
if (String.IsNullOrEmpty(username) ||
String.IsNullOrEmpty(password))
return false;
// Make sure the data source has been loaded
ReadMembershipDataStore();
// Validate the user name and password
MembershipUser user;
if (_Users.TryGetValue(username, out user))
{
if (user.Comment == password) // Case-sensitive
{
return true;
}
}
return false;
}
public override MembershipUser GetUser(string username,
bool userIsOnline)
{
// Note: This implementation ignores userIsOnline
// Validate input parameters
if (String.IsNullOrEmpty(username))
return null;
// Make sure the data source has been loaded
ReadMembershipDataStore();
// Retrieve the user from the data source
MembershipUser user;
if (_Users.TryGetValue(username, out user))
return user;
return null;
}
public override MembershipUserCollection GetAllUsers(int pageIndex,
int pageSize, out int totalRecords)
{
// Note: This implementation ignores pageIndex and pageSize,
// and it doesn't sort the MembershipUser objects returned
// Make sure the data source has been loaded
ReadMembershipDataStore();
MembershipUserCollection users =
new MembershipUserCollection();
foreach (KeyValuePair<string, MembershipUser> pair in _Users)
users.Add(pair.Value);
totalRecords = users.Count;
return users;
}
public override int GetNumberOfUsersOnline()
{
throw new NotSupportedException();
}
public override bool ChangePassword(string username,
string oldPassword, string newPassword)
{
throw new NotSupportedException();
}
public override bool
ChangePasswordQuestionAndAnswer(string username,
string password, string newPasswordQuestion,
string newPasswordAnswer)
{
throw new NotSupportedException();
}
public override MembershipUser CreateUser(string username,
string password, string email, string passwordQuestion,
string passwordAnswer, bool isApproved, object providerUserKey,
out MembershipCreateStatus status)
{
throw new NotSupportedException();
}
public override bool DeleteUser(string username,
bool deleteAllRelatedData)
{
throw new NotSupportedException();
}
public override MembershipUserCollection
FindUsersByEmail(string emailToMatch, int pageIndex,
int pageSize, out int totalRecords)
{
throw new NotSupportedException();
}
public override MembershipUserCollection
FindUsersByName(string usernameToMatch, int pageIndex,
int pageSize, out int totalRecords)
{
throw new NotSupportedException();
}
public override string GetPassword(string username, string answer)
{
throw new NotSupportedException();
}
public override MembershipUser GetUser(object providerUserKey,
bool userIsOnline)
{
throw new NotSupportedException();
}
public override string GetUserNameByEmail(string email)
{
throw new NotSupportedException();
}
public override string ResetPassword(string username,
string answer)
{
throw new NotSupportedException();
}
public override bool UnlockUser(string userName)
{
throw new NotSupportedException();
}
public override void UpdateUser(MembershipUser user)
{
throw new NotSupportedException();
}
// Helper method
private void ReadMembershipDataStore()
{
lock (this)
{
if (_Users == null)
{
_Users = new Dictionary<string, MembershipUser>
(16, StringComparer.InvariantCultureIgnoreCase);
XmlDocument doc = new XmlDocument();
doc.Load(_XmlFileName);
XmlNodeList nodes = doc.GetElementsByTagName("User");
foreach (XmlNode node in nodes)
{
MembershipUser user = new MembershipUser(
Name, // Provider name
node["UserName"].InnerText, // Username
null, // providerUserKey
node["Email"].InnerText, // Email
String.Empty, // passwordQuestion
node["Password"].InnerText, // Comment
true, // isApproved
false, // isLockedOut
DateTime.Now, // creationDate
DateTime.Now, // lastLoginDate
DateTime.Now, // lastActivityDate
DateTime.Now, // lastPasswordChangedDate
new DateTime(1980, 1, 1) // lastLockoutDate
);
_Users.Add(user.UserName, user);
}
}
}
}
}