Авторизация на основе ролей (VB)

Скотт Митчелл

Примечание

С момента написания этой статьи поставщики членства ASP.NET были заменены ASP.NET Identity. Мы настоятельно рекомендуем обновить приложения для использования платформы ASP.NET Identity , а не поставщиков членства, которые были представлены на момент написания этой статьи. ASP.NET Identity имеет ряд преимуществ по сравнению с системой членства ASP.NET, включая :

  • более высокая производительность;
  • Улучшенная расширяемость и тестируемость
  • Поддержка OAuth, OpenID Connect и двухфакторной проверки подлинности
  • Поддержка удостоверений на основе утверждений
  • Улучшенное взаимодействие с ASP.Net Core

Скачать код или скачать PDF-файл

Это руководство начинается с того, как платформа ролей связывает роли пользователя с контекстом безопасности. Затем в нем рассматривается применение правил авторизации URL-адресов на основе ролей. После этого мы рассмотрим использование декларативных и программных средств для изменения отображаемых данных и функциональных возможностей ASP.NET страницы.

Введение

В руководстве по авторизации на основе пользователя мы узнали, как использовать авторизацию URL-адреса, чтобы указать, какие пользователи могут посещать определенный набор страниц. С помощью небольшой разметки в Web.configмы можем указать ASP.NET разрешить только прошедшим проверку подлинности пользователям посещать страницу. Или мы можем диктовать, что только пользователи Tito и Bob были разрешены, или указать, что все пользователи, прошедшие проверку подлинности, кроме Сэма, были разрешены.

Помимо авторизации URL-адреса, мы также рассмотрели декларативные и программные методы управления отображаемыми данными и функциональностью страницы на основе посещения пользователем. В частности, мы создали страницу со списком содержимого текущего каталога. Любой пользователь может посетить эту страницу, но только пользователи, прошедшие проверку подлинности, могут просматривать содержимое файлов, и только Tito может удалить файлы.

Применение правил авторизации для каждого пользователя может перерасти в кошмар бухгалтерии. Более поддерживаемый подход заключается в использовании авторизации на основе ролей. Хорошей новостью является то, что имеющиеся в нашем распоряжении средства для применения правил авторизации одинаково хорошо работают с ролями, как и для учетных записей пользователей. Правила авторизации URL-адресов могут указывать роли вместо пользователей. Элемент управления LoginView, который отображает различные выходные данные для прошедших проверку подлинности и анонимных пользователей, можно настроить для отображения разного содержимого в зависимости от ролей пользователя, выполнившего вход. API ролей включает методы для определения ролей пользователя, выполнившего вход.

Это руководство начинается с того, как платформа ролей связывает роли пользователя с контекстом безопасности. Затем в нем рассматривается применение правил авторизации URL-адресов на основе ролей. После этого мы рассмотрим использование декларативных и программных средств для изменения отображаемых данных и функциональных возможностей ASP.NET страницы. Приступим к работе!

Основные сведения о связи ролей с контекстом безопасности пользователя

Каждый раз, когда запрос поступает в конвейер ASP.NET, он связывается с контекстом безопасности, который включает сведения, идентифицирующие инициатора запроса. При использовании проверки подлинности с помощью форм в качестве маркера удостоверения используется билет проверки подлинности. Как мы обсуждали в учебнике Обзор проверки подлинности на основе форм , FormsAuthenticationModule объект отвечает за определение удостоверения запрашивающей стороны, что и во время AuthenticateRequest события.

Если найден действительный билет проверки подлинности без срока действия, FormsAuthenticationModule он расшифровывается для установления личности запрашивающей стороны. Он создает новый GenericPrincipal объект и назначает его объекту HttpContext.User . Целью субъекта, например GenericPrincipal, является определение имени пользователя, прошедшего проверку подлинности, и ролей, к которому он принадлежит. Эта цель очевидна тем фактом, что все основные объекты имеют Identity свойство и IsInRole(roleName) метод. Однако FormsAuthenticationModuleобъект не заинтересован в записи сведений о роли, а в создаваемом GenericPrincipal им объекте роли не указываются.

Если платформа ролей включена, RoleManagerModule модуль HTTP выполняет шаги после FormsAuthenticationModule и определяет роли пользователя, прошедшего проверку подлинности, во время PostAuthenticateRequest события, которое срабатывает после AuthenticateRequest события. Если запрос получен от пользователя, прошедшего проверку подлинности RoleManagerModule , объект перезаписывает GenericPrincipal созданный FormsAuthenticationModule объектом объект и заменяет его RolePrincipal объектом . Класс RolePrincipal использует API ролей, чтобы определить, к каким ролям принадлежит пользователь.

На рисунке 1 показан рабочий процесс конвейера ASP.NET при использовании проверки подлинности на основе форм и платформы ролей. Выполняется FormsAuthenticationModule первым, идентифицирует пользователя с помощью запроса проверки подлинности и создает новый GenericPrincipal объект . Затем шаги RoleManagerModule в и перезаписывают GenericPrincipal объект RolePrincipal объектом .

Если анонимный пользователь посещает сайт, ни FormsAuthenticationModule не RoleManagerModule создает основной объект.

События конвейера ASP.NET для пользователя, прошедшего проверку подлинности на основе форм, и платформа ролей

Рис. 1. События конвейера ASP.NET для пользователя, прошедшего проверку подлинности на основе форм, и платформа ролей (щелкните для просмотра полноразмерного изображения)

Метод RolePrincipal объекта IsInRole(roleName) вызывает Roles.GetRolesForUser для получения ролей для пользователя, чтобы определить, является ли пользователь членом roleName. При использовании SqlRoleProviderэто приводит к запросу к базе данных хранилища ролей. При использовании правил RolePrincipalIsInRole авторизации URL-адресов на основе ролей метод будет вызываться при каждом запросе к странице, защищенной правилами авторизации URL-адресов на основе ролей. Вместо того, чтобы искать сведения о роли в базе данных при каждом запросе Roles , платформа включает возможность кэширования ролей пользователя в файле cookie.

Если платформа ролей настроена для кэширования ролей пользователя в файле cookie, RoleManagerModule объект создает файл cookie во время события конвейера EndRequestASP.NET. Этот файл cookie используется в последующих запросах в PostAuthenticateRequest, то есть при RolePrincipal создании объекта . Если файл cookie действителен и не истек, данные в файле cookie анализируются и используются для заполнения ролей пользователя, тем самым избавляя RolePrincipal от необходимости вызова Roles класса для определения ролей пользователя. На рисунке 2 показан этот рабочий процесс.

Сведения о роли пользователя могут храниться в файле cookie для повышения производительности

Рис. 2. Сведения о роли пользователя могут храниться в файле cookie для повышения производительности (щелкните для просмотра полноразмерного изображения)

По умолчанию механизм файлов cookie кэша ролей отключен. Его можно включить с помощью разметки <roleManager>конфигурации ; в Web.config. Мы обсуждали использование <roleManager> элемента для указания поставщиков ролей в учебнике По созданию ролей и управлению ими, поэтому этот элемент уже должен содержаться в файле приложенияWeb.config. Параметры файлов cookie кэша ролей указываются как атрибуты <roleManager>элемента ; и приведены в таблице 1.

Примечание

Параметры конфигурации, перечисленные в таблице 1, указывают свойства результирующего файла cookie кэша роли. Дополнительные сведения о файлах cookie, их работе и различных свойствах см. в этом руководстве по файлам cookie.

Свойство Описание
cacheRolesInCookie Логическое значение, указывающее, используется ли кэширование файлов cookie. По умолчанию — false.
cookieName Имя файла cookie кэша роли. Значение по умолчанию — ". ASPXROLES".
cookiePath Путь к файлу cookie имени ролей. Атрибут path позволяет разработчику ограничить область файла cookie определенной иерархией каталогов. Значение по умолчанию — "/", которое сообщает браузеру о том, что файл cookie билета проверки подлинности отправляется в любой запрос, выполненный в домен.
cookieProtection Указывает, какие методы используются для защиты файлов cookie кэша ролей. Допустимые значения: All (по умолчанию); Encryption; None; и Validation.md)

| cookieRequireSSL | Логическое значение, указывающее, требуется ли SSL-подключение для передачи файла cookie проверки подлинности. Значение по умолчанию — false cookieSlidingExpiration false createPersistentCookieis set totrue. | | cookieTimeout | Specifies the time, in minutes, after which the authentication ticket cookie expires. The default value is30. This value is only pertinent when createPersistentCookieis set totrue. | | createPersistentCookie | A Boolean value that specifies whether the role cache cookie is a session cookie or persistent cookie. Iffalse(the default), a session cookie is used, which is deleted when the browser is closed. If, a persistent cookie is used; it expires cookieTimeoutnumber of minutes after it has been created or after the previous visit, depending on the value ofcookieSlidingExpiration. | | домен домена| Specifies the cookie's domain value. The default value is an empty string, which causes the browser to use the domain from which it was issued (such as www.yourdomain.com). In this case, the cookie will <strong>not</strong> be sent when making requests to subdomains, such as admin.yourdomain.com. If you want the cookie to be passed to all subdomains you need to customize theattribute, setting it to "yourdomain.com". | | maxCachedResults | Specifies the maximum number of role names that are cached in the cookie. The default is 25. TheRoleManagerModuledoes not create a cookie for users that belong to more thanmaxCachedResultsroles. Consequently, theRolePrincipalobject'sIsInRolemethod will use theRolesclass to determine the user's roles. The reasonmaxCachedResultsexists is because many user agents do not permit cookies larger than 4,096 bytes. So this cap is meant to reduce the likelihood of exceeding this size limitation. If you have extremely long role names, you may want to consider specifying a smaller. This value is only pertinent when | A Boolean value that indicates whether the cookie's timeout is reset each time the user visits the site during a single session. The default value is. | | значение maxCachedResults; Напротив, если у вас очень короткие имена ролей, вы, вероятно, можете увеличить это значение. |

Таблица 1. Параметры конфигурации файла cookie кэша ролей

Давайте настроим приложение для использования файлов cookie кэша непостояных ролей. Для этого обновите <roleManager> элемент в , Web.config чтобы включить следующие атрибуты, связанные с файлами cookie:

<roleManager enabled="true" 
          defaultProvider="SecurityTutorialsSqlRoleProvider"
          cacheRolesInCookie="true"
          createPersistentCookie="false"
          cookieProtection="All">

     <providers>
     ...
     </providers>
</roleManager>

Элемент ; обновлен <roleManager>путем добавления трех атрибутов: cacheRolesInCookie, createPersistentCookieи cookieProtection. Если задать значение cacheRolesInCookietrue, RoleManagerModule теперь будет автоматически кэшировать роли пользователя в файле cookie вместо того, чтобы искать сведения о роли пользователя по каждому запросу. Я явно задал атрибутам createPersistentCookiefalse и cookieProtection значение и Allсоответственно. Технически мне не нужно было указывать значения для этих атрибутов, так как я просто назначил им значения по умолчанию, но я поместил их здесь, чтобы четко понять, что я не использую постоянные файлы cookie и что файл cookie зашифрован и проверен.

Вот и все! В дальнейшем платформа ролей будет кэшировать роли пользователей в файлах cookie. Если браузер пользователя не поддерживает файлы cookie или если файлы cookie удаляются или теряются, это не имеет большого значения — RolePrincipal объект будет просто использовать Roles класс в случае, если файл cookie (или недопустимый или просроченный) недоступен.

Примечание

Группа Microsoft Patterns & Practices не рекомендует использовать файлы cookie кэша постоянных ролей. Так как владение файлом cookie кэша ролей достаточно для подтверждения членства в роли, если злоумышленник может каким-то образом получить доступ к файлу cookie действительного пользователя, он может олицетворять этого пользователя. Вероятность этого увеличивается, если файл cookie сохраняется в браузере пользователя. Дополнительные сведения об этой рекомендации по безопасности, а также о других проблемах безопасности см. в разделе Список вопросов безопасности для ASP.NET 2.0.

Шаг 1. Определение правил авторизации Role-Based URL-адресов

Как описано в руководстве по авторизации на основе пользователей , авторизация по URL-адресу позволяет ограничить доступ к набору страниц на основе пользователя или роли. Правила авторизации URL-адресов изложены в Web.config использовании элемента с дочерними <allow> элементами <authorization> и <deny> . В дополнение к правилам авторизации, связанным с пользователями, которые обсуждались в предыдущих руководствах, каждый <allow> и <deny> дочерний элемент также может включать:

  • Определенная роль
  • Список ролей с разделителями-запятыми

Например, правила авторизации URL-адресов предоставляют доступ пользователям с ролями "Администраторы" и "Руководители", но запрещают доступ всем остальным:

<authorization>

     <allow roles="Administrators, Supervisors" />
     <deny users="*" />
</authorization>

Элемент <allow> в приведенной выше разметке указывает, что роли Администраторы и Руководители разрешены; <deny>элемент ; указывает, что все пользователи отклоняются.

Давайте настроим наше приложение таким образом, чтобы ManageRoles.aspxстраницы , UsersAndRoles.aspxи CreateUserWizardWithRoles.aspx были доступны только тем пользователям в роли администраторов, а RoleBasedAuthorization.aspx страница оставалась доступной для всех посетителей.

Для этого начните с Web.config добавления файла в папку Roles .

Добавление файла Web.config в каталог ролей

Рис. 3. Добавление Web.config файла в Roles каталог (щелкните для просмотра полноразмерного изображения)

Затем добавьте следующую разметку конфигурации в Web.config:

<?xml version="1.0"?>

<configuration>
     <system.web>
          <authorization>
               <allow roles="Administrators" />
               <deny users="*"/>
          </authorization>

     </system.web>

     <!-- Allow all users to visit RoleBasedAuthorization.aspx -->
     <location path="RoleBasedAuthorization.aspx">
          <system.web>
               <authorization>
                    <allow users="*" />

               </authorization>
          </system.web>
     </location>
</configuration>

Элемент <authorization> в <system.web> разделе указывает, что доступ к ASP.NET ресурсам в каталоге могут получить только пользователи с ролью Roles "Администраторы". Элемент <location> определяет альтернативный набор правил авторизации URL-адресов для RoleBasedAuthorization.aspx страницы, позволяя всем пользователям посещать страницу.

После сохранения изменений Web.configв войдите в систему от имени пользователя, не являющегося администратором, а затем попробуйте посетить одну из защищенных страниц. Будет UrlAuthorizationModule обнаруживать, что у вас нет разрешения на посещение запрошенного ресурса; следовательно, FormsAuthenticationModule будет перенаправлять вас на страницу входа. Страница входа перенаправит вас на страницу UnauthorizedAccess.aspx (см. рис. 4). Последнее перенаправление со страницы UnauthorizedAccess.aspx входа в происходит из-за кода, добавленного на страницу входа на шаге 2 руководства по авторизации на основе пользователей. В частности, страница входа автоматически перенаправляет любого пользователя, прошедшего проверку подлинности, на UnauthorizedAccess.aspx , если строка запроса содержит ReturnUrl параметр, так как этот параметр указывает, что пользователь пришел на страницу входа после попытки просмотреть страницу, на которую он не был авторизован.

Только пользователи с ролью

Рис. 4. Только пользователи с ролью "Администраторы" могут просматривать защищенные страницы (щелкните для просмотра полноразмерного изображения)

Выйдите из системы, а затем войдите в систему от имени пользователя с ролью "Администраторы". Теперь вы сможете просматривать три защищенные страницы.

Тито может посетить страницу UsersAndRoles.aspx, так как он имеет роль

Рис. 5. Тито может посетить страницу UsersAndRoles.aspx , так как он находится в роли администраторов (щелкните для просмотра полноразмерного изображения)

Примечание

При указании правил авторизации URL-адресов для ролей или пользователей важно помнить, что правила анализируются по одному сверху вниз. Как только совпадение найдено, пользователю предоставляется или запрещается доступ в зависимости от того, было ли найдено совпадение в элементе <allow> или <deny> . Если совпадение не найдено, пользователю предоставляется доступ. Следовательно, если вы хотите ограничить доступ одной или несколькими учетными записями пользователей, необходимо использовать <deny> элемент в качестве последнего элемента в конфигурации авторизации URL-адресов. Если правила авторизации URL-адресов не включают<deny>элемент, всем пользователям будет предоставлен доступ. Более подробное обсуждение того, как анализируются правила авторизации URL-адресов, см. в разделе "Использование UrlAuthorizationModule правил авторизации для предоставления или запрета доступа" руководства по авторизации на основе пользователей.

Шаг 2. Ограничение функциональных возможностей на основе ролей вошедшего в систему пользователя

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

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

Давайте создадим страницу со списком всех учетных записей пользователей в системе в GridView. GridView будет включать имя пользователя, адрес электронной почты, дату последнего входа и комментарии о пользователе. Помимо отображения сведений о каждом пользователе, GridView будет включать возможности редактирования и удаления. Сначала мы создадим эту страницу с функцией редактирования и удаления, доступной всем пользователям. В разделах "Использование элемента управления LoginView" и "Программное ограничение функциональных возможностей" мы посмотрим, как включить или отключить эти функции в зависимости от роли посещающего пользователя.

Примечание

На странице ASP.NET, который мы собираемся создать, для отображения учетных записей пользователей используется элемент управления GridView. Так как в этой серии учебников основное внимание уделяется проверке подлинности на основе форм, авторизации, учетным записям пользователей и ролям, я не хочу тратить слишком много времени на обсуждение внутренней работы элемента управления GridView. Хотя в этом руководстве содержатся пошаговые инструкции по настройке этой страницы, в нем не рассматриваются сведения о том, почему были сделаны определенные варианты или какое влияние на отображаемые выходные данные оказывают определенные свойства. Для тщательного изучения элемента управления GridView проверка мою серию руководств по работе с данными в ASP.NET 2.0.

Начните с открытия страницы RoleBasedAuthorization.aspx в папке Roles . Перетащите Элемент GridView со страницы на Designer и задайте для нее ID значение UserGrid. Через некоторое время мы напишем код, который вызывает Membership.GetAllUsers метод и привязывает результирующий MembershipUserCollection объект к GridView. содержит MembershipUserCollection объект для каждой MembershipUser учетной записи пользователя в системе; MembershipUser объекты имеют такие свойства,LastLoginDate как UserName,Email и т. д.

Перед написанием кода, который привязывает учетные записи пользователей к сетке, давайте сначала определим поля GridView. В смарт-теге GridView щелкните ссылку "Изменить столбцы", чтобы открыть диалоговое окно Поля (см. рис. 6). Здесь снимите флажок "Автоматическое создание полей" в левом нижнем углу. Так как мы хотим, чтобы gridView включал возможности редактирования и удаления, добавьте CommandField и задайте для его ShowEditButton свойств и ShowDeleteButton значение True. Затем добавьте четыре поля для отображения UserNameсвойств , Email, LastLoginDateи Comment . Используйте BoundField для двух свойств только для чтения (UserName и LastLoginDate) и TemplateFields для двух редактируемых полей (Email и Comment).

Первое свойство BoundField отображается UserName ; для его HeaderText свойств и DataField задайте значение UserName. Это поле не будет изменяться, поэтому задайте для его ReadOnly свойства значение True. LastLoginDate Настройте BoundField, задав для параметра HeaderText значение "Last Login", DataField а для параметра — значение LastLoginDate. Отформатируем выходные данные BoundField так, чтобы отображалась только дата (вместо даты и времени). Для этого присвойте HtmlEncode свойству BoundField значение False, а свойству DataFormatString — значение "{0:d}". Кроме того, присвойте свойству ReadOnly значение True.

HeaderText Задайте для свойств двух TemplateFields значения "Email" и "Комментарий".

Поля GridView можно настроить с помощью диалогового окна

Рис. 6. Поля GridView можно настроить с помощью диалогового окна "Поля" (щелкните для просмотра полноразмерного изображения)

Теперь необходимо определить ItemTemplate и EditItemTemplate для полей шаблонов "Email" и "Комментарий". Добавьте элемент управления Label Web в каждый из ItemTemplates и привяжите их Text свойства к свойствам Email и Comment соответственно.

Для Email TemplateField добавьте элемент TextBox с именем Email в объект EditItemTemplate и привяжите его Text свойство к свойству Email с помощью двусторонней привязки данных. Добавьте RequiredFieldValidator и RegularExpressionValidator в EditItemTemplate , чтобы убедиться, что пользователь, изменяющий свойство Email, ввел допустимый адрес электронной почты. В поле TemplateField "Comment" добавьте многострочный элемент TextBox с EditItemTemplateименем Comment . Задайте для свойств TextBox Columns и Rows значения 40 и 4 соответственно, а затем привяжите его Text свойство к свойству Comment с помощью двусторонней привязки данных.

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

<asp:TemplateField HeaderText="Email">
     <ItemTemplate>
          <asp:Label runat="server" ID="Label1" Text='<%# Eval("Email")%>'></asp:Label>

     </ItemTemplate>
     <EditItemTemplate>
          <asp:TextBox runat="server" ID="Email" Text='<%# Bind("Email")%>'></asp:TextBox>

          <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" 
               ControlToValidate="Email" Display="Dynamic"
               ErrorMessage="You must provide an email address."
               SetFocusOnError="True">*</asp:RequiredFieldValidator>

          <asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server"
               ControlToValidate="Email" Display="Dynamic"
               ErrorMessage="The email address you have entered is not valid. Please fix 
               this and try again."
               SetFocusOnError="True"

               ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">*
          </asp:RegularExpressionValidator>
     </EditItemTemplate>
</asp:TemplateField>

<asp:TemplateField HeaderText="Comment">
     <ItemTemplate>
          <asp:Label runat="server" ID="Label2" Text='<%# Eval("Comment")%>'></asp:Label>

     </ItemTemplate>
     <EditItemTemplate>
          <asp:TextBox runat="server" ID="Comment" TextMode="MultiLine"
               Columns="40" Rows="4" Text='<%# Bind("Comment")%>'>

          </asp:TextBox>
     </EditItemTemplate>
</asp:TemplateField>

При изменении или удалении учетной записи пользователя необходимо знать значение свойства этого пользователя UserName . Задайте для свойства GridView DataKeyNames значение UserName, чтобы эти сведения были доступны в коллекции GridView DataKeys .

Наконец, добавьте на страницу элемент управления ValidationSummary и задайте для его ShowMessageBox свойства значение True, а для свойства ShowSummary — значение False. При использовании этих параметров validationSummary отображает оповещение на стороне клиента, если пользователь пытается изменить учетную запись пользователя с отсутствующим или недопустимым адресом электронной почты.

<asp:ValidationSummary ID="ValidationSummary1"
               runat="server"
               ShowMessageBox="True"
               ShowSummary="False" />

Теперь мы завершили декларативную разметку этой страницы. Следующая задача — привязать набор учетных записей пользователей к GridView. Добавьте метод с именем BindUserGrid в RoleBasedAuthorization.aspx класс кода программной части страницы, который привязывает MembershipUserCollection объект , возвращаемый свойством Membership.GetAllUsersUserGrid GridView. Вызовите этот метод из обработчика Page_Load событий при первом посещении страницы.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
     If Not Page.IsPostBack Then
          BindUserGrid()
     End If
End Sub

Private Sub BindUserGrid()
     Dim allUsers As MembershipUserCollection = Membership.GetAllUsers()
     UserGrid.DataSource = allUsers
     UserGrid.DataBind()
End Sub

Используя этот код, перейдите на страницу через браузер. Как показано на рисунке 7, вы увидите в GridView сведения о каждой учетной записи пользователя в системе.

Элемент UserGrid GridView выводит сведения о каждом пользователе в системе.

Рис. 7. GridView UserGrid выводит сведения о каждом пользователе в системе (щелкните для просмотра полноразмерного изображения)

Примечание

GridView UserGrid выводит список всех пользователей в интерфейсе без страниц. Этот простой интерфейс сетки не подходит для сценариев с несколькими десятками пользователей. Одним из вариантов является настройка GridView для включения разбиения по страницам. Метод Membership.GetAllUsers имеет две перегрузки: одна, которая не принимает входные параметры и возвращает всех пользователей, а другая принимает целочисленные значения для индекса страницы и размера страницы и возвращает только указанное подмножество пользователей. Вторая перегрузка может использоваться для более эффективного перебора пользователей, так как она возвращает только точное подмножество учетных записей пользователей, а не все из них. Если у вас тысячи учетных записей пользователей, можно рассмотреть интерфейс на основе фильтра, который отображает только тех пользователей, имя пользователя которых начинается с выбранного символа, например. МетодMembership.FindUsersByName идеально подходит для создания пользовательского интерфейса на основе фильтров. Мы рассмотрим создание такого интерфейса в следующем руководстве.

Элемент управления GridView обеспечивает встроенную поддержку редактирования и удаления, если элемент управления привязан к правильно настроенной системе управления источником данных, например SqlDataSource или ObjectDataSource. Однако UserGrid GridView имеет программную привязку данных, поэтому для выполнения этих двух задач необходимо написать код. В частности, необходимо создать обработчики событий GridView RowEditing, RowCancelingEdit, RowUpdatingи RowDeleting , которые активируются, когда посетитель нажимает кнопки Изменить, Отменить, Обновить или Удалить GridView.

Начните с создания обработчиков событий для событий GridView RowEditing, RowCancelingEditи , а RowUpdating затем добавьте следующий код:

Protected Sub UserGrid_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles UserGrid.RowEditing
     ' Set the grid's EditIndex and rebind the data

     UserGrid.EditIndex = e.NewEditIndex
     BindUserGrid()
End Sub

Protected Sub UserGrid_RowCancelingEdit(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCancelEditEventArgs) Handles UserGrid.RowCancelingEdit
     ' Revert the grid's EditIndex to -1 and rebind the data
     UserGrid.EditIndex = -1
     BindUserGrid()
End Sub
    
Protected Sub UserGrid_RowUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) Handles UserGrid.RowUpdating
     ' Exit if the page is not valid
     If Not Page.IsValid Then
          Exit Sub
     End If

     ' Determine the username of the user we are editing
     Dim UserName As String = UserGrid.DataKeys(e.RowIndex).Value.ToString()

     ' Read in the entered information and update the user
     Dim EmailTextBox As TextBox = CType(UserGrid.Rows(e.RowIndex).FindControl("Email"),TextBox)
     Dim CommentTextBox As TextBox= CType(UserGrid.Rows(e.RowIndex).FindControl("Comment"),TextBox)

     ' Return information about the user
     Dim UserInfo As MembershipUser = Membership.GetUser(UserName)

     ' Update the User account information
     UserInfo.Email = EmailTextBox.Text.Trim()
     UserInfo.Comment = CommentTextBox.Text.Trim()

     Membership.UpdateUser(UserInfo)

     ' Revert the grid's EditIndex to -1 and rebind the data
     UserGrid.EditIndex = -1
     BindUserGrid()
End Sub

RowEditing Обработчики событий и RowCancelingEdit просто задают свойство GridViewEditIndex, а затем повторно привязывает список учетных записей пользователей к сетке. Интересные вещи происходят в обработчике RowUpdating событий. Этот обработчик событий начинается с проверки допустимости данных, а затем получает UserName значение измененной учетной записи пользователя из DataKeys коллекции. Затем Email ссылки на и Comment TextBoxes в двух TemplateFields EditItemTemplate создаются программными средствами. Их Text свойства содержат измененный адрес электронной почты и комментарий.

Чтобы обновить учетную запись пользователя с помощью API членства, необходимо сначала получить сведения о пользователе, которые мы делаем через вызов Membership.GetUser(userName). Затем свойства и Comment возвращаемого MembershipUser объекта Email обновляются значениями, введенными в два элемента TextBox из интерфейса редактирования. Наконец, эти изменения сохраняются с помощью вызова Membership.UpdateUser. Обработчик RowUpdating событий завершается путем восстановления GridView к интерфейсу предварительного редактирования.

Затем создайте RowDeleting обработчик событий RowDeleting и добавьте следующий код:

Protected Sub UserGrid_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles UserGrid.RowDeleting

     ' Determine the username of the user we are editing
     Dim UserName As String = UserGrid.DataKeys(e.RowIndex).Value.ToString()

     ' Delete the user
     Membership.DeleteUser(UserName)

     ' Revert the grid's EditIndex to -1 and rebind the data
     UserGrid.EditIndex = -1
     BindUserGrid()
End Sub

Приведенный выше обработчик событий начинается с захвата UserName значения из коллекции GridViewDataKeys. Затем это UserName значение передается в метод класса DeleteUserMembership. Метод DeleteUser удаляет учетную запись пользователя из системы, включая связанные данные о членстве (например, к каким ролям принадлежит этот пользователь). После удаления пользователя для сетки EditIndex устанавливается значение -1 (если пользователь нажал кнопку Удалить, когда другая строка находилась в режиме BindUserGrid редактирования), и вызывается метод .

Примечание

Кнопка Удалить не требует подтверждения от пользователя перед удалением учетной записи пользователя. Я призываю вас добавить некоторую форму подтверждения пользователя, чтобы уменьшить вероятность случайного удаления учетной записи. Одним из самых простых способов подтверждения действия является диалоговое окно подтверждения на стороне клиента. Дополнительные сведения об этом методе см. в статье Добавление подтверждения Client-Side при удалении.

Убедитесь, что эта страница работает должным образом. Вы сможете изменить адрес электронной почты и комментарий любого пользователя, а также удалить любую учетную запись пользователя. RoleBasedAuthorization.aspx Так как страница доступна всем пользователям, любой пользователь , даже анонимный посетители, может посетить эту страницу и изменить и удалить учетные записи пользователей! Давайте обновим эту страницу, чтобы только пользователи с ролями "Руководители" и "Администраторы" могли изменять адрес электронной почты и комментарий пользователя, а только администраторы могут удалять учетную запись пользователя.

В разделе "Использование элемента управления LoginView" рассматривается использование элемента управления LoginView для отображения инструкций, относящихся к роли пользователя. Если пользователь с ролью "Администраторы" посещает эту страницу, мы отобразим инструкции по изменению и удалению пользователей. Если пользователь с ролью "Руководители" достигает этой страницы, мы отобразим инструкции по редактированию пользователей. А если посетитель является анонимным или не имеет роли "Руководители" или "Администраторы", появится сообщение о том, что он не может изменять или удалять сведения об учетной записи пользователя. В разделе "Программное ограничение функциональных возможностей" мы напишем код, который программным способом отображает или скрывает кнопки Изменить и Удалить на основе роли пользователя.

Использование элемента управления LoginView

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

Начните с добавления LoginView над UserGrid GridView. Как мы уже говорили ранее, элемент управления LoginView имеет два встроенных шаблона: AnonymousTemplate и LoggedInTemplate. Введите краткое сообщение в обоих этих шаблонах, информирующее пользователя о том, что он не может изменять или удалять какие-либо сведения о пользователе.

<asp:LoginView ID="LoginView1" runat="server">
     <LoggedInTemplate>
          You are not a member of the Supervisors or Administrators roles. Therefore you
           cannot edit or delete any user information.
     </LoggedInTemplate>
     <AnonymousTemplate>

          You are not logged into the system. Therefore you cannot edit or delete any user
           information.
     </AnonymousTemplate>
</asp:LoginView>

В дополнение к AnonymousTemplate и LoggedInTemplateэлемент управления LoginView может включать RoleGroups, которые являются шаблонами для конкретных ролей. Каждая группа ролей содержит одно свойство , которое указывает, Rolesк каким ролям применяется RoleGroup. Для Roles свойства можно задать одну роль (например, "Администраторы") или список ролей с разделителями-запятыми (например, "Администраторы, руководители").

Чтобы управлять группами RoleGroups, щелкните ссылку "Изменить группы ролей" в смарт-теге элемента управления, чтобы открыть редактор коллекции RoleGroup. Добавьте две новые группы ролей. Задайте для первого свойства RoleGroup Roles значение "Администраторы", а для второго — "Руководители".

Управление шаблонами Role-Specific LoginView с помощью редактора коллекции RoleGroup

Рис. 8. Управление шаблонами Role-Specific LoginView с помощью редактора коллекции RoleGroup (щелкните для просмотра полноразмерного изображения)

Нажмите кнопку ОК, чтобы закрыть редактор коллекции RoleGroup; При этом декларативная разметка LoginView включает раздел с дочерним элементом <RoleGroups><asp:RoleGroup> для каждой группы RoleGroup, определенной в редакторе коллекции RoleGroup. Кроме того, раскрывающийся список "Представления" в смарт-теге LoginView, который изначально перечислял только AnonymousTemplate и LoggedInTemplate , теперь также включает добавленные группы ролей.

Измените roleGroups таким образом, чтобы пользователи в роли "Руководители" отображали инструкции по изменению учетных записей пользователей, а пользователи с ролью "Администраторы" — инструкции по редактированию и удалению. После внесения этих изменений декларативная разметка LoginView должна выглядеть следующим образом.

<asp:LoginView ID="LoginView1" runat="server">
     <RoleGroups>
          <asp:RoleGroup Roles="Administrators">

               <ContentTemplate>
                    As an Administrator, you may edit and delete user accounts. 
                    Remember: With great power comes great responsibility!
               </ContentTemplate>
          </asp:RoleGroup>
          <asp:RoleGroup Roles="Supervisors">
               <ContentTemplate>
                    As a Supervisor, you may edit users&#39; Email and Comment information. 
                    Simply click the Edit button, make your changes, and then click Update.
               </ContentTemplate>

          </asp:RoleGroup>
     </RoleGroups>
     <LoggedInTemplate>
          You are not a member of the Supervisors or Administrators roles. 
          Therefore you cannot edit or delete any user information.
     </LoggedInTemplate>
     </AnonymousTemplate>
          You are not logged into the system. 
          Therefore you cannot edit or delete any user information.
     </AnonymousTemplate>
</asp:LoginView>

После внесения этих изменений сохраните страницу и посетите ее в браузере. Сначала посетите страницу в качестве анонимного пользователя. Должно появиться сообщение "Вы не вошли в систему. Поэтому вы не можете изменять или удалять какие-либо сведения о пользователе". Затем войдите в систему как пользователь, прошедший проверку подлинности, но не имеющий роли "Руководители" или "Администраторы". На этот раз вы увидите сообщение"Вы не являетесь членом ролей "Руководители или Администраторы". Поэтому вы не можете изменять или удалять какие-либо сведения о пользователе".

Затем войдите в систему от имени пользователя, являющегося членом роли "Руководители". На этот раз вы увидите сообщение о ролевом руководстве (см. рис. 9). А если вы входите в систему как пользователь с ролью Администраторы, вы увидите сообщение о роли администраторов (см. рис. 10).

Брюсу отображается сообщение Role-Specific руководителей

Рис. 9. Брюсу отображается сообщение Role-Specific руководителей (щелкните для просмотра полноразмерного изображения)

Тито отображается сообщение Role-Specific администраторов

Рис. 10. Tito отображается сообщение администраторов Role-Specific (щелкните для просмотра полноразмерного изображения)

Как показано на снимках экрана на рисунках 9 и 10, LoginView отображает только один шаблон, даже если применяется несколько шаблонов. Bruce и Tito являются пользователями, вошедши в систему, но LoginView отображает только соответствующую группу LoggedInTemplateRoleGroup, а не . Кроме того, Тито принадлежит к ролям "Администраторы" и "Руководители", но элемент управления LoginView отображает шаблон ролей "Администраторы" вместо шаблона "Руководители".

На рисунке 11 показан рабочий процесс, используемый элементом управления LoginView для определения шаблона для отрисовки. Обратите внимание, что если указано несколько групп RoleGroup, шаблон LoginView отрисовывает первую соответствующую группу RoleGroup. Другими словами, если бы мы поместили группу ролей "Руководители" в качестве первой, а администраторы — в качестве второй, то, когда Тито посетил эту страницу, он увидит сообщение "Руководители".

Рабочий процесс элемента управления LoginView для определения шаблона для отрисовки

Рис. 11. Рабочий процесс элемента управления LoginView для определения шаблона для отрисовки (щелкните для просмотра полноразмерного изображения)

Программное ограничение функциональных возможностей

Хотя в элементе управления LoginView отображаются различные инструкции в зависимости от роли пользователя, посещающего страницу, кнопки Изменить и Отмена остаются видимыми для всех пользователей. Нам необходимо программно скрыть кнопки Изменить и Удалить для анонимных посетителей и пользователей, которые не имеют ни роли руководителей, ни администраторов. Нам нужно скрыть кнопку Удалить для всех, кто не является администратором. Для этого мы напишем немного кода, который программно ссылается на элементы CommandField's Edit и Delete LinkButtons и при необходимости задает их Visible свойствам Falseзначение .

Самый простой способ программной ссылки на элементы управления в CommandField — сначала преобразовать их в шаблон. Для этого щелкните ссылку "Изменить столбцы" в смарт-теге GridView, выберите CommandField из списка текущих полей и щелкните ссылку "Преобразовать это поле в TemplateField". Это преобразует CommandField в TemplateField с и ItemTemplateEditItemTemplate. содержит ItemTemplate элементы Edit и Delete LinkButtons, в то время как в EditItemTemplate нем размещаются элементы LinkButtons Update и Cancel.

Преобразование CommandField в templateField

Рис. 12. Преобразование CommandField в templateField (щелкните для просмотра полноразмерного изображения)

Обновите элемент Управления и Удаление ссылок в ItemTemplate, задав для их ID свойств значения EditButton и DeleteButtonсоответственно.

<asp:TemplateField ShowHeader="False">
     <EditItemTemplate>
          <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="True" 
               CommandName="Update" Text="Update"></asp:LinkButton>

           <asp:LinkButton ID="LinkButton2" runat="server" CausesValidation="False"
               CommandName="Cancel" Text="Cancel"></asp:LinkButton>

     </EditItemTemplate>
     <ItemTemplate>
          <asp:LinkButton ID="EditButton" runat="server" CausesValidation="False" 
               CommandName="Edit" Text="Edit"></asp:LinkButton>

           <asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False"
               CommandName="Delete" Text="Delete"></asp:LinkButton>

     </ItemTemplate>
</asp:TemplateField>

Всякий раз, когда данные привязаны к GridView, GridView перечисляет записи в своем DataSource свойстве и создает соответствующий GridViewRow объект. При создании RowCreated каждого GridViewRow объекта запускается событие . Чтобы скрыть кнопки "Изменить" и "Удалить" для неавторизованных пользователей, необходимо создать обработчик событий для этого события и программно ссылаться на кнопки Изменить и Удалить linkButtons, задав соответствующие Visible свойства.

Создайте обработчик события и RowCreated добавьте следующий код:

Protected Sub UserGrid_RowCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles UserGrid.RowCreated
     If e.Row.RowType = DataControlRowType.DataRow AndAlso e.Row.RowIndex <> UserGrid.EditIndex Then
          ' Programmatically reference the Edit and Delete LinkButtons
          Dim EditButton As LinkButton = CType(e.Row.FindControl("EditButton"), LinkButton)

          Dim DeleteButton As LinkButton = CType(e.Row.FindControl("DeleteButton"), LinkButton)

          EditButton.Visible = (User.IsInRole("Administrators") OrElse User.IsInRole("Supervisors"))
          DeleteButton.Visible = User.IsInRole("Administrators")
     End If
End Sub

Помните, что RowCreated событие срабатывает для всех строк GridView, включая верхний колонтитул, нижний колонтитул, интерфейс пейджера и т. д. Мы хотим программно ссылаться на кнопки Изменить и Удалить, только если мы имеем дело со строкой данных, не в режиме редактирования (так как в строке в режиме редактирования есть кнопки Обновить и Отмена, а не Изменить и Удалить). Эта проверка обрабатывается оператором If .

Если мы имеем дело со строкой данных, которая не находится в режиме редактирования, ссылки на элементы Edit и Delete LinkButtons задаются, а их Visible свойства задаются на основе логических значений, возвращаемых методом User объекта IsInRole(roleName) . Объект User ссылается на субъект, созданный RoleManagerModuleобъектом ; следовательно, метод использует API ролей, чтобы определить, IsInRole(roleName) принадлежит ли текущий посетитель к roleName.

Примечание

Мы могли бы использовать класс Roles напрямую, заменив вызов User.IsInRole(roleName) на вызов Roles.IsUserInRole(roleName) метода . В этом примере я решил использовать метод основного IsInRole(roleName) объекта, так как он более эффективен, чем использование API ролей напрямую. Ранее в этом руководстве мы настроили диспетчер ролей для кэширования ролей пользователя в файле cookie. Эти кэшированные данные cookie используются только при вызове метода субъекта IsInRole(roleName) . Прямые вызовы API ролей всегда связаны с поездкой в хранилище ролей. Даже если роли не кэшируются в файле cookie, вызов метода основного объекта IsInRole(roleName) обычно более эффективен, так как при первом вызове во время запроса он кэширует результаты. API ролей, с другой стороны, не выполняет кэширование. RowCreated Так как событие запускается один раз для каждой строки в GridView, использование User.IsInRole(roleName) включает только одну поездку в хранилище ролей, тогда как Roles.IsUserInRole(roleName) требует N поездок, где N — количество учетных записей пользователей, отображаемых в сетке.

Свойство кнопки Visible "Изменить" имеет значение , True если пользователь, посещая эту страницу, имеет роль "Администраторы" или "Руководители"; в противном случае ему присваивается значение False. Свойство кнопки Visible "Удалить" имеет значение True только в том случае, если пользователь имеет роль "Администраторы".

Проверьте эту страницу в браузере. Если вы посещаете страницу как анонимный посетитель или как пользователь, который не является ни руководителем, ни администратором, commandField будет пустым; он по-прежнему существует, но в виде тонкой щепки без кнопок "Изменить" или "Удалить".

Примечание

Можно полностью скрыть CommandField, если страницу посещает не руководитель и администратор. Я оставляю это как упражнение для читателя.

Кнопки

Рис. 13. Кнопки "Изменить" и "Удалить" скрыты для пользователей, не являющихся руководителями и неадминистраторами (щелкните для просмотра полноразмерного изображения)

Если пользователь, принадлежащий к роли "Супервизоры" (но не к роли "Администраторы"), посещает его, он видит только кнопку Изменить.

Хотя кнопка

Рис. 14. Хотя кнопка "Изменить" доступна для руководителей, кнопка "Удалить" скрыта (щелкните, чтобы просмотреть полноразмерное изображение)

А если администратор посещает, у него есть доступ к кнопкам Изменить и Удалить.

Кнопки

Рис. 15. Кнопки "Изменить" и "Удалить" доступны только для администраторов (щелкните для просмотра полноразмерного изображения)

Шаг 3. Применение правил авторизации Role-Based к классам и методам

На шаге 2 мы ограничили возможности редактирования пользователями с ролями "Супервизоры" и "Администраторы" и удаляем возможности только для администраторов. Это было достигнуто путем скрытия связанных элементов пользовательского интерфейса для неавторизованных пользователей с помощью программных методов. Такие меры не гарантируют, что несанкционированный пользователь не сможет выполнить привилегированное действие. Элементы пользовательского интерфейса могут быть добавлены позже или которые мы забыли скрыть для неавторизованных пользователей. Или злоумышленник может обнаружить другой способ получить страницу ASP.NET для выполнения нужного метода.

Простой способ убедиться, что неавторизованный пользователь не сможет получить доступ к определенной части функциональности, — украсить этот класс или метод атрибутомPrincipalPermission . Когда среда выполнения .NET использует класс или выполняет один из своих методов, она проверяет, имеет ли текущий контекст безопасности разрешение. Атрибут PrincipalPermission предоставляет механизм, с помощью которого можно определить эти правила.

Мы рассмотрели использование атрибута PrincipalPermission в руководстве по авторизации на основе пользователя. В частности, мы узнали, как украсить обработчик событий GridView SelectedIndexChanged и RowDeleting так, чтобы они могли выполняться только пользователями, прошедшими проверку подлинности, и Tito соответственно. Атрибут PrincipalPermission также работает с ролями.

Давайте продемонстрируем использование атрибута PrincipalPermission в обработчиках событий GridView RowUpdating и RowDeleting , чтобы запретить выполнение для неавторизоваемых пользователей. Все, что нам нужно сделать, это добавить соответствующий атрибут в каждое определение функции:

<PrincipalPermission(SecurityAction.Demand, Role:="Administrators")>_
<PrincipalPermission(SecurityAction.Demand, Role:="Supervisors")>_
Protected Sub UserGrid_RowUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) Handles UserGrid.RowUpdating
     ...
End Sub

<PrincipalPermission(SecurityAction.Demand, Role:="Administrators")>_
Protected Sub UserGrid_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles UserGrid.RowDeleting
     ...
End Sub

Атрибут обработчика RowUpdating событий определяет, что обработчик событий может выполнять только пользователи с ролями "Администраторы" или "Супервизоры", где в качестве атрибута RowDeleting в обработчике событий выполнение ограничивается пользователями с ролью "Администраторы".

Примечание

Атрибут PrincipalPermission представлен в виде класса в System.Security.Permissions пространстве имен . Не забудьте добавить Imports System.Security.Permissions оператор в начало файла класса кода программной части, чтобы импортировать это пространство имен.

Если каким-либо образом неадминистратор пытается выполнить RowDeleting обработчик событий или если обработчик событий не является руководителем или администратором RowUpdating , среда выполнения .NET вызовет SecurityException.

Если контекст безопасности не авторизован для выполнения метода, создается исключение SecurityException.

Рис. 16. Если контекст безопасности не авторизован для выполнения метода, SecurityException возникает исключение (щелкните, чтобы просмотреть полноразмерное изображение)

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

Дополнительные сведения об использовании атрибута PrincipalPermission для определения правил авторизации для классов и методов см. в записи блога Скотта ГатриДобавление правил авторизации в уровни бизнеса и данных с помощью PrincipalPermissionAttributes.

Сводка

В этом руководстве мы рассмотрели, как указать грубые и детализированные правила авторизации на основе ролей пользователя. ASP. Функция авторизации URL-адресов NET позволяет разработчику страниц указать, какие удостоверения разрешен или запрещен доступ к тем или иным страницам. Как мы уже видели в руководстве по авторизации на основе пользователя, правила авторизации URL-адресов можно применять для каждого пользователя. Их также можно применять на основе ролей, как показано на шаге 1 этого руководства.

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

Счастливое программирование!

Дополнительные материалы

Дополнительные сведения по темам, рассматриваемым в этом руководстве, см. в следующих ресурсах:

Об авторе

Скотт Митчелл (Scott Mitchell), автор нескольких книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Teach Yourself ASP.NET 2.0 в 24 часа. Скотт можно связаться по адресу mitchell@4guysfromrolla.com или через его блог по адресу http://ScottOnWriting.NET.

Отдельная благодарность...

Эта серия учебников была проверена многими полезными рецензентами. К ведущим рецензентам этого руководства относятся Сучи Банерджи и Тетера Мерфи. Хотите ознакомиться с моими предстоящими статьями MSDN? Если да, бросить мне линию на mitchell@4GuysFromRolla.com