Авторизация на основе ролей (C#)
Примечание
С момента написания этой статьи поставщики членства 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 разрешить только прошедшим проверку подлинности пользователям посещать страницу. Или можно диктовать, что разрешены только пользователи Тито и Боб, или указать, что разрешены все пользователи, прошедшие проверку подлинности, за исключением Сэма.
Помимо авторизации по 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
объект субъекта не создает.
Рис. 1. События конвейера ASP.NET для пользователя, прошедшего проверку подлинности на основе форм, и платформа ролей (щелкните для просмотра полноразмерного изображения)
Кэширование сведений о роли в файле cookie
Метод RolePrincipal
объекта IsInRole(roleName)
вызывается Roles.GetRolesForUser
для получения ролей для пользователя, чтобы определить, является ли пользователь членом roleName. При использовании SqlRoleProvider
это приводит к запросу к базе данных хранилища ролей. При использовании правил RolePrincipal
IsInRole
авторизации URL-адресов на основе ролей метод будет вызываться при каждом запросе к странице, защищенной правилами авторизации URL-адресов на основе ролей. Вместо того, чтобы искать сведения о ролях в базе данных при каждом запросе, платформа ролей включает возможность кэширования ролей пользователя в файле cookie.
Если платформа ролей настроена для кэширования ролей пользователя в файле cookie, RoleManagerModule
создается файл cookie во время события конвейера EndRequest
ASP.NET. Этот файл cookie используется в последующих запросах в PostAuthenticateRequest
, то есть при RolePrincipal
создании объекта . Если файл cookie действителен и не истек, данные в файле cookie анализируются и используются для заполнения ролей пользователя, тем самым избавляя RolePrincipal
от необходимости выполнять вызов Roles
к классу для определения ролей пользователя. На рисунке 2 показан этот рабочий процесс.
Рис. 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 . |
cookieRequireSSL |
Логическое значение, указывающее, требуется ли SSL-подключение для передачи файла cookie проверки подлинности. Значение по умолчанию — false . |
cookieSlidingExpiration |
Логическое значение, указывающее, сбрасывается ли время ожидания файла cookie каждый раз, когда пользователь посещает сайт в течение одного сеанса. Значение по умолчанию — false . Это значение уместно только в том случае, если createPersistentCookie задано значение true . |
cookieTimeout |
Указывает время (в минутах), по истечении которого истекает срок действия файла cookie билета проверки подлинности. Значение по умолчанию — 30 . Это значение уместно только в том случае, если createPersistentCookie задано значение true . |
createPersistentCookie |
Логическое значение, указывающее, является ли файл cookie кэша роли файлом cookie сеанса или постоянным файлом cookie. Если false (по умолчанию), используется файл cookie сеанса, который удаляется при закрытии браузера. Если true задано значение , используется постоянный файл cookie, срок действия которого истекает в минутах cookieTimeout после создания или после предыдущего посещения в зависимости от значения cookieSlidingExpiration . |
domain |
Указывает значение домена файла cookie. Значением по умолчанию является пустая строка, которая заставляет браузер использовать домен, из которого он был выдан (например , www.yourdomain.com). В этом случае файл cookie не будет отправляться при выполнении запросов к поддоменам, таким как admin.yourdomain.com. Если вы хотите, чтобы файл cookie передавался всем поддоменам, необходимо настроить domain атрибут , установив для него значение "yourdomain.com". |
maxCachedResults |
Указывает максимальное количество имен ролей, кэшированных в файле cookie. Значение по умолчанию — 25. не RoleManagerModule создает файл cookie для пользователей, принадлежащих не только maxCachedResults ролям. Следовательно, RolePrincipal метод объекта IsInRole будет использовать Roles класс для определения ролей пользователя. Причина maxCachedResults заключается в том, что многие агенты пользователей не разрешают файлы cookie размером более 4096 байт. Таким образом, это ограничение предназначено для снижения вероятности превышения этого ограничения размера. Если у вас очень длинные имена ролей, может потребоваться указать меньшее 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
. Если задать значение cacheRolesInCookie
true
, RoleManagerModule
теперь будет автоматически кэшировать роли пользователя в файле cookie вместо того, чтобы искать сведения о роли пользователя по каждому запросу. Я явно задал атрибутам createPersistentCookie
false
и 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
статье Использование <authorization>
элемента с дочерними <allow>
элементами и <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
.
Рис. 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. Только пользователи с ролью "Администраторы" могут просматривать защищенные страницы (щелкните для просмотра полноразмерного изображения)
Выйдите из системы, а затем войдите в систему как пользователь с ролью Администраторы. Теперь вы сможете просматривать три защищенные страницы.
Рис. 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
объекты имеют такие свойства, как UserName
, Email
, LastLoginDate
и т. д.
Перед написанием кода, который привязывает учетные записи пользователей к сетке, давайте сначала определим поля 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. Настройте BoundField, задав для нее LastLoginDate
HeaderText
значение "Last Login", а для параметра DataField
— значение LastLoginDate. Давайте отформатируем выходные данные этого BoundField так, чтобы отображалась только дата (вместо даты и времени). Для этого задайте для свойства BoundField HtmlEncode
значение False, а для свойства DataFormatString
— значение "{0:d}". Также присвойте свойству ReadOnly
значение True.
HeaderText
Задайте для свойств двух templateFields значения "Email" и "Комментарий".
Рис. 6. Поля GridView можно настроить с помощью диалогового окна Поля (щелкните для просмотра полноразмерного изображения)
Теперь необходимо определить ItemTemplate
и EditItemTemplate
для шаблонов TemplateField Email и Comment. Добавьте веб-элемент управления Label в каждый элемент ItemTemplate
управления и привяжите их Text
свойства к свойствам Email
и Comment
соответственно.
Для Email TemplateField добавьте элемент TextBox с именем Email
EditItemTemplate
и привяжите его Text
свойство к свойству Email
с помощью двусторонней привязки данных. Добавьте RequiredFieldValidator и RegularExpressionValidator в EditItemTemplate
, чтобы убедиться, что посетитель, изменяющий свойство Email, ввел допустимый адрес электронной почты. Для поля TemplateField "Comment" добавьте многострочный элемент TextBox с именем Comment
в .EditItemTemplate
Задайте для свойств 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.GetAllUsers
к UserGrid
GridView. Вызовите этот метод из обработчика Page_Load
событий при первом посещении страницы.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
BindUserGrid();
}
private void BindUserGrid()
{
MembershipUserCollection allUsers = Membership.GetAllUsers();
UserGrid.DataSource = allUsers;
UserGrid.DataBind();
}
После этого кода перейдите на страницу через браузер. Как показано на рисунке 7, вы увидите GridView со списком сведений о каждой учетной записи пользователя в системе.
Рис. 7. GridView UserGrid
содержит сведения о каждом пользователе в системе (щелкните для просмотра полноразмерного изображения)
Примечание
GridView UserGrid
выводит список всех пользователей в интерфейсе без страниц. Этот простой интерфейс сетки не подходит для сценариев, в которых есть несколько десятков или более пользователей. Один из вариантов — настроить GridView для включения разбиения по страницам. Метод Membership.GetAllUsers
имеет две перегрузки: одна, которая не принимает входные параметры и возвращает всех пользователей, а другая принимает целочисленные значения для индекса страницы и размера страницы и возвращает только указанное подмножество пользователей. Вторая перегрузка может использоваться для более эффективного перебора пользователей, так как она возвращает только точное подмножество учетных записей пользователей, а не все из них. Если у вас тысячи учетных записей пользователей, можно рассмотреть интерфейс на основе фильтра, который отображает только тех пользователей, имя пользователя которых начинается с выбранного символа. идеально Membership.FindUsersByName method
подходит для создания пользовательского интерфейса на основе фильтров. Мы рассмотрим создание такого интерфейса в следующем руководстве.
Элемент управления GridView обеспечивает встроенную поддержку редактирования и удаления, если элемент управления привязан к правильно настроенной системе управления источником данных, например SqlDataSource или ObjectDataSource. GridView UserGrid
, однако, имеет программную привязку данных, поэтому для выполнения этих двух задач необходимо написать код. В частности, необходимо создать обработчики событий GridView RowEditing
, RowCancelingEdit
, RowUpdating
и RowDeleting
, которые активируются, когда посетитель нажимает кнопки Изменить, Отмена, Обновить или Удалить GridView.
Начните с создания обработчиков событий для событий GridView RowEditing
, RowCancelingEdit
и , а RowUpdating
затем добавьте следующий код:
protected void UserGrid_RowEditing(object sender, GridViewEditEventArgs e)
{
// Set the grid's EditIndex and rebind the data
UserGrid.EditIndex = e.NewEditIndex;
BindUserGrid();
}
protected void UserGrid_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
// Revert the grid's EditIndex to -1 and rebind the data
UserGrid.EditIndex = -1;
BindUserGrid();
}
protected void UserGrid_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
// Exit if the page is not valid
if (!Page.IsValid)
return;
// Determine the username of the user we are editing
string UserName = UserGrid.DataKeys[e.RowIndex].Value.ToString();
// Read in the entered information and update the user
TextBox EmailTextBox = UserGrid.Rows[e.RowIndex].FindControl("Email") as TextBox;
TextBox CommentTextBox = UserGrid.Rows[e.RowIndex].FindControl("Comment") as TextBox;
// Return information about the user
MembershipUser UserInfo = 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();
}
RowEditing
Обработчики событий и RowCancelingEdit
просто задают свойство GridViewEditIndex
, а затем повторно привязывал список учетных записей пользователей к сетке. Интересные вещи происходят в обработчике RowUpdating
событий. Этот обработчик событий начинается с проверки допустимости данных, а затем извлекает UserName
значение измененной учетной записи пользователя из DataKeys
коллекции. Затем Email
программными ссылками на и Comment
TextBoxes в двух templateFields EditItemTemplate
. Их Text
свойства содержат измененный адрес электронной почты и комментарий.
Чтобы обновить учетную запись пользователя с помощью API членства, необходимо сначала получить сведения о пользователе, что мы делаем через вызов Membership.GetUser(userName)
. Затем свойства возвращаемого MembershipUser
Email
объекта и Comment
обновляются значениями, введенными в два элемента TextBox из интерфейса редактирования. Наконец, эти изменения сохраняются с помощью вызова Membership.UpdateUser
. Обработчик RowUpdating
событий завершает работу, возвращая GridView к интерфейсу предварительного редактирования.
Затем создайте RowDeleting
обработчик событий и добавьте следующий код:
protected void UserGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// Determine the username of the user we are editing
string UserName = 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();
}
Приведенный выше обработчик событий начинается с захвата UserName
значения из коллекции GridViewDataKeys
. Затем это UserName
значение передается в метод класса DeleteUser
Membership. Метод 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, которые являются шаблонами для конкретных ролей. Каждая группа RoleGroup содержит одно свойство , которое указывает, Roles
к каким ролям применяется RoleGroup. Для Roles
свойства можно задать одну роль (например, "Администраторы") или список ролей с разделителями-запятыми (например, "Администраторы, руководители").
Чтобы управлять группами RoleGroups, щелкните ссылку Edit RoleGroups (Изменить группы ролей) в смарт-теге элемента управления, чтобы открыть редактор коллекции RoleGroup. Добавьте две новые группы ролей. Задайте для первого свойства RoleGroup Roles
значение "Администраторы", а для второго — значение "Супервизоры".
Рис. 8. Управление шаблонами Role-Specific LoginView с помощью редактора коллекций RoleGroup (щелкните для просмотра полноразмерного изображения)
Нажмите кнопку ОК, чтобы закрыть редактор коллекции RoleGroup; Обновляет декларативную разметку LoginView, включив раздел с дочерним элементом <RoleGroups>
<asp:RoleGroup>
для каждой группы RoleGroup, определенной в редакторе коллекции RoleGroup. Кроме того, раскрывающийся список "Представления" в смарт-теге LoginView, который изначально перечислял только AnonymousTemplate
и LoggedInTemplate
, теперь также включает добавленные группы RoleGroups.
Измените 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' 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).
Рис. 9. Брюс отображает сообщение Role-Specific руководителей (щелкните, чтобы просмотреть полноразмерное изображение)
Рис. 10. Тито отображается сообщение "Администраторы Role-Specific" (щелкните для просмотра полноразмерного изображения)
Как показано на снимках экрана на рис. 9 и 10, LoginView отображает только один шаблон, даже если применяется несколько шаблонов. Брюс и Тито являются пользователями, вошедшего в систему, но LoginView отображает только соответствующую группу RoleGroup, а не LoggedInTemplate
. Кроме того, Тито принадлежит к ролям "Администраторы" и "Супервизоры", но элемент управления LoginView отображает шаблон для конкретной роли "Администраторы", а не "Супервизоры".
На рисунке 11 показан рабочий процесс, используемый элементом управления LoginView для определения шаблона для отрисовки. Обратите внимание, что если указано несколько групп RoleGroup, шаблон LoginView отрисовывает первую соответствующую группу RoleGroup. Иными словами, если бы мы поместили группу ролей руководителей в качестве первой, а администраторы — в качестве второй, то, когда Тито посетил эту страницу, он увидит сообщение "Руководители".
Рис. 11. Рабочий процесс элемента управления LoginView для определения шаблона для отображения (щелкните для просмотра полноразмерного изображения)
Программное ограничение функциональности
Хотя элемент управления LoginView отображает различные инструкции в зависимости от роли пользователя, посещающего страницу, кнопки Изменить и Отмена остаются видимыми для всех пользователей. Нам нужно программно скрыть кнопки Правка и Удалить для анонимных посетителей и пользователей, которые не имеют ни роли руководителей, ни администраторов. Нам нужно скрыть кнопку Удалить для всех, кто не является администратором. Для этого мы напишем небольшой код, который программным способом ссылается на элементы CommandField's Edit и Delete LinkButtons и при необходимости задает их Visible
свойствам false
значение .
Самый простой способ программной ссылки на элементы управления в CommandField — сначала преобразовать их в шаблон. Для этого щелкните ссылку "Изменить столбцы" в смарт-теге GridView, выберите CommandField из списка текущих полей и щелкните ссылку "Преобразовать это поле в TemplateField". Это преобразует CommandField в TemplateField с и ItemTemplate
EditItemTemplate
. Содержит ItemTemplate
элементы Edit и Delete LinkButtons, а в ней EditItemTemplate
размещаются элементы Update и Cancel LinkButtons.
Рис. 12. Преобразование CommandField в TemplateField (щелкните, чтобы просмотреть полноразмерное изображение)
Обновите кнопки Edit и Delete LinkButtons в 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
объекта запускается событие . Чтобы скрыть кнопки "Изменить" и "Удалить" для неавторизованных пользователей, необходимо создать обработчик событий для этого события и программно сослаться на кнопки Edit и Delete LinkButtons, задав соответствующие Visible
свойства.
Создайте обработчик события и RowCreated
добавьте следующий код:
protected void UserGrid_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow && e.Row.RowIndex != UserGrid.EditIndex)
{
// Programmatically reference the Edit and Delete LinkButtons
LinkButton EditButton = e.Row.FindControl("EditButton") as LinkButton;
LinkButton DeleteButton = e.Row.FindControl("DeleteButton") as LinkButton;
EditButton.Visible = (User.IsInRole("Administrators") || User.IsInRole("Supervisors"));
DeleteButton.Visible = User.IsInRole("Administrators");
}
}
Помните, что RowCreated
событие срабатывает для всех строк GridView, включая верхний колонтитул, нижний колонтитул, интерфейс пейджера и т. д. Мы хотим программно ссылаться на кнопки Edit и Delete LinkButtons, только если мы имеем дело со строкой данных, не в режиме редактирования (так как в строке в режиме редактирования вместо правки есть кнопки "Обновить" и "Отмена", а не "Изменить" и "Удалить"). Эта проверка обрабатывается оператором if
.
Если мы имеем дело со строкой данных, которая не находится в режиме редактирования, ссылки на элементы Edit и Delete LinkButtons задаются, а их Visible
свойства задаются на основе логических значений, возвращаемых методом User
объекта IsInRole(roleName)
. Объект User ссылается на субъект, созданный с помощью RoleManagerModule
; следовательно, IsInRole(roleName)
метод использует API ролей, чтобы определить, принадлежит ли текущий посетитель к 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 void UserGrid_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
...
}
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
protected void UserGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
...
}
Атрибут обработчика RowUpdating
событий определяет, что обработчик событий может выполнять только пользователи с ролями "Администраторы" или "Супервизоры", где в качестве атрибута RowDeleting
в обработчике событий выполнение ограничивается пользователями с ролью "Администраторы".
Примечание
Атрибут PrincipalPermission
представлен в виде класса в System.Security.Permissions
пространстве имен . Не забудьте добавить using System.Security.Permissions
оператор в начало файла класса кода программной части, чтобы импортировать это пространство имен.
Если каким-либо образом неадминистратор пытается выполнить RowDeleting
обработчик событий или если обработчик событий не является руководителем или администратором RowUpdating
, среда выполнения .NET вызовет SecurityException
.
Рис. 16. Если контекст безопасности не авторизован для выполнения метода, SecurityException
возникает исключение (щелкните, чтобы просмотреть полноразмерное изображение)
Помимо ASP.NET страниц, многие приложения также имеют архитектуру, которая включает различные слои, такие как бизнес-логика и уровни доступа к данным. Эти уровни обычно реализуются как библиотеки классов и предлагают классы и методы для выполнения функциональных возможностей, связанных с бизнес-логикой и данными. Атрибут PrincipalPermission
также полезен для применения правил авторизации к этим уровням.
Дополнительные сведения об использовании атрибута PrincipalPermission
для определения правил авторизации для классов и методов см. в записи блога Скотта ГатриДобавление правил авторизации в уровни бизнеса и данных с помощью PrincipalPermissionAttributes
.
Сводка
В этом руководстве мы рассмотрели, как указать грубые и детализированные правила авторизации на основе ролей пользователя. ASP. Функция авторизации URL-адресов NET позволяет разработчику страниц указать, какие удостоверения разрешен или запрещен доступ к тем или иным страницам. Как мы уже видели в руководстве по авторизации на основе пользователя, правила авторизации URL-адресов можно применять для каждого пользователя. Их также можно применять на основе ролей, как показано на шаге 1 этого руководства.
Правила детальной авторизации могут применяться декларативно или программно. На шаге 2 мы рассмотрели использование функции RoleGroups элемента управления LoginView для отображения различных выходных данных в зависимости от ролей посещающих пользователей. Мы также рассмотрели способы программного определения того, принадлежит ли пользователь определенной роли и как соответствующим образом настроить функциональность страницы.
Счастливое программирование!
Дополнительные материалы
Дополнительные сведения по темам, рассматриваемым в этом руководстве, см. в следующих ресурсах:
- Добавление правил авторизации в бизнес-уровни и уровни данных с помощью
PrincipalPermissionAttributes
- Изучение членства, ролей и профиля ASP.NET 2.0: работа с ролями
- Список вопросов безопасности для ASP.NET 2.0
- Техническая документация по элементу
<roleManager>
Об авторе
Скотт Митчелл (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