Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Замечание
С тех пор как эта статья была написана, поставщики членства ASP.NET были заменены ASP.NET Identity. Настоятельно рекомендуется обновлять приложения для использования платформы ASP.NET Identity, а не поставщиков членства ASP.NET, которые были представлены на момент написания этой статьи. ASP.NET Identity имеет ряд преимуществ по сравнению с системой членства ASP.NET, в том числе:
- Улучшенная производительность
- Улучшенная расширяемость и возможность тестирования
- Поддержка OAuth, OpenID Connect и двухфакторной проверки подлинности
- Поддержка индентификации на основе утверждений
- Улучшение взаимодействия с ASP.Net Core
Скачивание кода или скачивание PDF
В этом руководстве описывается, как платформа ролей связывает роли пользователя с его контекстом безопасности. Затем он проверяет, как применять правила авторизации URL-адресов на основе ролей. После этого мы рассмотрим использование декларативных и программных средств для изменения отображаемых данных и функций, предлагаемых страницей ASP.NET.
Введение
В руководстве по авторизации, основанной на пользователе мы узнали, как использовать URL-авторизацию, чтобы указать, какие пользователи могут посещать определённые страницы. С помощью небольшой разметки в Web.config, мы могли бы указать ASP.NET разрешить аутентифицированным пользователям посещать страницу. Или мы можем диктовать, что разрешены только пользователи Tito и Bob, или указать, что все прошедшие проверку подлинности пользователи, кроме Sam, были разрешены.
Помимо авторизации URL-адреса, мы также рассмотрели декларативные и программные методы управления отображаемыми данными и функциональными возможностями, предлагаемыми страницей на основе посещения пользователя. В частности, мы создали страницу, которая содержит содержимое текущего каталога. Любой пользователь может посетить эту страницу, но только прошедшие проверку подлинности пользователи могут просматривать содержимое файлов, и только Tito может удалить файлы.
Применение правил авторизации на индивидуальной основе для каждого пользователя может превратиться в административный кошмар. Более доступный подход — использовать авторизацию на основе ролей. Хорошая новость заключается в том, что средства в нашем распоряжении для применения правил авторизации работают одинаково хорошо с ролями, как и для учетных записей пользователей. Правила авторизации URL-адресов могут указывать роли вместо пользователей. Элемент управления LoginView, который отображает различные выходные данные для прошедших проверку подлинности и анонимных пользователей, можно настроить для отображения разного содержимого на основе ролей пользователя, вошедшего в систему. API ролей включает методы для определения ролей пользователя, вошедшего в систему.
В этом руководстве описывается, как платформа ролей связывает роли пользователя с его контекстом безопасности. Затем он проверяет, как применять правила авторизации URL-адресов на основе ролей. После этого мы рассмотрим использование декларативных и программных средств для изменения отображаемых данных и функций, предлагаемых страницей ASP.NET. Давайте приступим!
Общие сведения о том, как роли связаны с контекстом безопасности пользователя
Каждый раз, когда запрос входит в конвейер ASP.NET, он связывается с контекстом безопасности, который включает сведения, идентифицирующие инициатора запроса. При использовании аутентификации с помощью форм, билет проверки подлинности используется в качестве токена удостоверения. Как мы обсуждали в Обзоре проверки подлинности форм, FormsAuthenticationModule отвечает за определение личности запрашивающего, что происходит во время AuthenticateRequest события.
Если найдется действительное удостоверение подлинности, срок действия которого не истек, FormsAuthenticationModule декодирует его, чтобы определить удостоверение запрашивающего пользователя. Он создает новый GenericPrincipal объект и назначает его объекту HttpContext.User . Цель главного, например GenericPrincipal, заключается в определении имени пользователя, прошедшего проверку подлинности, и каким ролям он принадлежит. Эта цель очевидна тем, что все основные объекты имеют Identity свойство и IsInRole(roleName) метод. Однако FormsAuthenticationModule, не заинтересован в фиксации информации о ролях, и объект GenericPrincipal, который он создает, не указывает роли.
Если платформа ролей включена, HTTP-модуль RoleManagerModule вступает в действие после FormsAuthenticationModule и идентифицирует роли пользователей, прошедших аутентификацию, во время события PostAuthenticateRequest, которое запускается после события AuthenticateRequest. Если запрос выполнен от пользователя, прошедшего проверку подлинности, то функция перезаписывает объект GenericPrincipal, созданный FormsAuthenticationModule, и заменяет его объектом RolePrincipal. Класс RolePrincipal использует API ролей, чтобы определить, к каким ролям принадлежит пользователь.
На рисунке 1 показан рабочий процесс ASP.NET конвейера при использовании проверки подлинности форм и платформы ролей. Сначала FormsAuthenticationModule выполняется, идентифицирует пользователя через её запрос проверки подлинности и создает новый объект GenericPrincipal. Затем RoleManagerModule вводится и перезаписывает объект GenericPrincipal объектом RolePrincipal.
Если анонимный пользователь посещает сайт, ни FormsAuthenticationModule, ни RoleManagerModule не создают объект предиката.
Рис. 1. События конвейера ASP.NET для прошедшего проверку подлинности пользователя при использовании проверки подлинности форм и платформы ролей (щелкните, чтобы просмотреть изображение полного размера)
Кэширование сведений о роли в файле cookie
Метод IsInRole(roleName) объекта RolePrincipal вызывает Roles.
GetRolesForUser чтобы получить роли пользователя, чтобы определить, является ли пользователь членом roleName. При использовании SqlRoleProviderэто приводит к запросу к базе данных хранилища ролей. При использовании правил RolePrincipalIsInRole авторизации URL-адресов на основе ролей метод будет вызываться при каждом запросе на страницу, защищенную правилами авторизации URL-адресов на основе ролей. Вместо того чтобы искать сведения о роли в базе данных по каждому запросу, платформа Roles включает возможность кэшировать роли пользователя в файле 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 имени ролей. Атрибут пути позволяет разработчику ограничить область файла cookie определенной иерархией каталогов. Значение по умолчанию — "/", которое сообщает браузеру отправить файл cookie запроса проверки подлинности любому запросу, сделанному в домен. |
cookieProtection |
Указывает, какие методы используются для защиты файла cookie кэша ролей. Допустимые значения: All (по умолчанию); Encryption; Noneи Validation.md) |
|
cookieRequireSSL | Логическое значение, указывающее, требуется ли SSL-подключение для передачи файла cookie проверки подлинности. Значение по умолчанию — false. | | cookieSlidingExpiration | 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 isfalse createPersistentCookie. This value is only pertinent when trueis set tocookieTimeout. | | 30 | Specifies the time, in minutes, after which the authentication ticket cookie expires. The default value iscreatePersistentCookie true. This value is only pertinent when createPersistentCookieis set to. | | false | A Boolean value that specifies whether the role cache cookie is a session cookie or persistent cookie. If(the default), a session cookie is used, which is deleted when the browser is closed. Iftrue, 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. | | domain| 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 thanmaxCachedResults RolePrincipalroles. Consequently, theIsInRoleobject'sRolesmethod will use themaxCachedResultsclass to determine the user's roles. The reasonexists 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значение maxCachedResults; Наоборот, если у вас очень короткие имена ролей, возможно, увеличить это значение. |
Таблица 1: Параметры конфигурации кэша cookie для ролей
Давайте настроим наше приложение для использования несохраняемых файлов cookie кэша ролей. Для этого обновите элемент <roleManager> в Web.config, чтобы включить следующие атрибуты, связанные с cookies:
<roleManager enabled="true"
defaultProvider="SecurityTutorialsSqlRoleProvider"
cacheRolesInCookie="true"
createPersistentCookie="false"
cookieProtection="All">
<providers>
...
</providers>
</roleManager>
Я обновил <roleManager>элемент ; путем добавления трех атрибутов: cacheRolesInCookie, createPersistentCookieи cookieProtection. При установке значения cacheRolesInCookie в true, RoleManagerModule теперь автоматически кэширует роли пользователя в файле cookie вместо запроса сведений о роли пользователя при каждом запросе. Я явно задал атрибуты createPersistentCookie и cookieProtection значениями false и All соответственно. Технически я не должен указывать значения для этих атрибутов, так как я только что назначил их значениям по умолчанию, но я поместил их здесь, чтобы сделать его явным образом ясно, что я не использую постоянные файлы cookie, и что файл cookie как зашифрован, так и проверен.
Вот и все! Поэтому платформа ролей кэширует роли пользователей в файлах cookie. Если браузер пользователя не поддерживает файлы cookie, или если их файлы cookie удаляются или теряются, каким-то образом, это не имеет большого значения. RolePrincipal Объект просто будет использовать Roles класс в том случае, если файл cookie (или недопустимый или истекший срок действия) недоступен.
Замечание
Группа Microsoft Patterns & Practices не рекомендует использовать файлы cookie для постоянного кэширования ролей. Так как обладание файлом cookie кэша ролей достаточно для подтверждения членства в роли, если хакер каким-либо способом сможет получить доступ к файлу cookie валидного пользователя, он может выдавать себя за этого пользователя. Вероятность этого увеличивается, если файл cookie сохраняется в браузере пользователя. Дополнительные сведения об этой рекомендации по безопасности, а также о других проблемах безопасности см. в списке вопросов безопасности для ASP.NET 2.0.
Шаг 1. Определение правил авторизации 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 руководства User-Based Authorization. В частности, страница входа автоматически перенаправляет всех прошедших проверку подлинности пользователей UnauthorizedAccess.aspx , если строка запроса содержит ReturnUrl параметр, так как этот параметр указывает, что пользователь прибыл на страницу входа после попытки просмотреть страницу, которую он не был авторизован для просмотра.
Рис. 4. Только пользователи в роли "Администраторы" могут просматривать защищенные страницы (щелкните, чтобы просмотреть изображение полного размера)
Выйдите из учетной записи, а затем войдите как пользователь, имеющий роль 'Администратор'. Теперь вы сможете просматривать три защищенных страницы.
Рис. 5. Tito может посетить UsersAndRoles.aspx страницу, так как он в роли администратора (Нажмите, чтобы увидеть изображение в полном размере)
Замечание
При указании правил авторизации URL-адресов ( для ролей или пользователей) важно учитывать, что правила анализируются по одному за раз, начиная с верхнего уровня. Как только совпадение найдено, пользователю предоставляется доступ или запрещается доступ в зависимости от того, найдено ли совпадение в элементе <allow> или <deny>. Если соответствующее совпадение не найдено, доступ предоставляется пользователю. Следовательно, если требуется ограничить доступ к одной или нескольким учетным записям пользователей, необходимо использовать <deny> элемент в качестве последнего элемента в конфигурации авторизации URL-адреса.
Если правила авторизации URL-адреса не включают<deny>элемент, всем пользователям будет предоставлен доступ. Для более подробного рассмотрения того, как UrlAuthorizationModule использует правила авторизации URL-адресов, обратитесь к разделу "Рассмотрение того, как UrlAuthorizationModule использует правила авторизации для предоставления или отказа в доступе" руководства попользовательской авторизации.
Шаг 2. Ограничение функциональных возможностей на основе ролей пользователя, вошедшего в систему
Авторизация URL позволяет легко указать общие правила авторизации, которые определяют, какие удостоверения имеют разрешение и какие запрещены для просмотра определенной страницы (или всех страниц в папке и её подпапках). Однако в некоторых случаях мы можем разрешить всем пользователям посещать страницу, но ограничить функциональные возможности страницы на основе ролей пользователей. Это может влечь за собой отображение или скрытие данных на основе роли пользователя или предоставление дополнительных функций пользователям, принадлежащим определенной роли.
Подробные правила авторизации, основанные на ролях, могут быть реализованы декларативно или программно, или с помощью их сочетания. В следующем разделе мы рассмотрим, как реализовать декларативную точечную авторизацию через элемент управления LoginView. После этого мы рассмотрим программные методы. Прежде чем мы перейдём к применению правил точной детализации авторизации, сначала необходимо создать страницу, функциональность которой зависит от роли пользователя, посещающего её.
Давайте создадим страницу, которая перечисляет все учетные записи пользователей в системе в GridView. GridView будет включать имя пользователя, адрес электронной почты, дату последнего входа и комментарии о пользователе. Помимо отображения сведений о каждом пользователе, GridView будет включать возможности редактирования и удаления. Сначала мы создадим эту страницу с функциональностью редактирования и удаления, доступной всем пользователям. В разделах "Использование элемента управления LoginView" и "Программное ограничение функциональных возможностей" мы увидим, как включить или отключить эти функции на основе роли пользователя.
Замечание
На странице ASP.NET мы планируем создать элемент управления GridView для отображения учетных записей пользователей. Так как в этом руководстве основное внимание уделяется проверке подлинности форм, авторизации, учетным записям пользователей и ролям, я не хочу тратить слишком много времени на обсуждение внутренних действий элемента управления GridView. Хотя в этом руководстве содержатся конкретные пошаговые инструкции по настройке этой страницы, оно не рассматривает в деталях, почему были сделаны определенные выборы, и какой эффект оказывают отдельные свойства на отрендеренный вывод. Для тщательного изучения элемента управления GridView ознакомьтесь с моим руководством по работе с данными в ASP.NET 2.0 .
Начните с открытия RoleBasedAuthorization.aspx страницы в папке Roles . Перетащите GridView со страницы на конструктор и установите его 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. Настройте компонент LastLoginDate BoundField, установив для свойства HeaderText значение "Last Login" и для свойства DataField значение "LastLoginDate". Отформатируем выходные данные этого BoundField таким образом, чтобы отображалась только дата (вместо даты и времени). Для этого задайте для свойства BoundField HtmlEncode значение False, а его DataFormatString свойство — "{0:d}". Также задайте свойству ReadOnly значение True.
Задайте для двух TemplateFields значения "Email" и "Comment".
Рис. 6. Поля GridView можно настроить с помощью диалогового окна "Поля" (щелкните, чтобы просмотреть изображение полного размера)
Теперь необходимо определить ItemTemplate и EditItemTemplate для полей "Электронная почта" и "Комментарий" TemplateFields. Добавьте элемент управления Label к каждому из ItemTemplates и привяжите их свойства Text к свойствам Email и Comment соответственно.
Для TemplateField "Email" добавьте TextBox с именем Email, а затем привяжите его свойство Text к свойству Email с помощью двусторонней привязки данных. Добавьте RequiredFieldValidator и RegularExpressionValidator в EditItemTemplate, чтобы убедиться, что пользователь, изменяющий свойство Email, ввел допустимый адрес электронной почты. Для поля "Комментарий" типа TemplateField добавьте многострочное текстовое поле с именем Comment в его EditItemTemplate. Установите для свойств Columns и Rows элемента TextBox значения 40 и 4 соответственно, а затем привяжите его свойство Text к свойству Comment с помощью двусторонней синхронизации данных.
После настройки этих templateFields их декларативная разметка должна выглядеть следующим образом:
<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, к элементу GridView UserGrid. Вызовите этот метод из обработчика 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 со сведениями о каждой учетной записи пользователя в системе.
Рис. 7.UserGrid GridView выводит сведения о каждом пользователе в системе (щелкните, чтобы просмотреть изображение полного размера)
Замечание
UserGrid GridView отображает всех пользователей в интерфейсе без разбивки на страницы. Этот простой интерфейс сетки не подходит для сценариев, в которых существует несколько десятков пользователей. Одним из вариантов является настройка GridView для включения разбиения по страницам. Метод Membership.GetAllUsers имеет две перегрузки: один из них не принимает входные параметры и возвращает всех пользователей и тот, который принимает целые значения для индекса страницы и размера страницы, а также возвращает только указанное подмножество пользователей. Вторая перегрузка может быть использована для более эффективного перехода по страницам пользователей, так как она возвращает лишь точное подмножество учетных записей пользователей, а не все из них. Если у вас тысячи учетных записей пользователей, может потребоваться рассмотреть интерфейс на основе фильтра, который отображает только тех пользователей, имя пользователя которых начинается с выбранного символа, например.
ЭтотMembership.FindUsersByName метод идеально подходит для создания пользовательского интерфейса на основе фильтра. Мы рассмотрим создание такого интерфейса в будущем руководстве.
Элемент управления GridView предлагает встроенную поддержку редактирования и удаления, если элемент управления привязан к правильно настроенной системе управления источниками данных, например SqlDataSource или ObjectDataSource. Однако UserGrid GridView программно привязан к данным. Поэтому необходимо написать код для выполнения этих двух задач. В частности, необходимо создать обработчики событий для событий RowEditing, RowCancelingEdit, RowUpdating, и RowDeleting в GridView, которые запускаются при нажатии пользователем на кнопки "Изменить", "Отмена", "Обновить" или "Удалить".
Сначала создайте обработчики событий для событий RowEditing, RowCancelingEdit и RowUpdating GridView, а затем добавьте следующий код:
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 обработчики событий просто задают свойство GridView EditIndex, а затем повторно привязывают список учетных записей пользователей к сетке. Интересные вещи происходят в обработчике RowUpdating событий. Этот обработчик событий начинается с проверки допустимости данных, а затем получает UserName значение измененной учетной записи пользователя из DataKeys коллекции. Затем текстовые поля Email и Comment в двух TemplateFields EditItemTemplate ссылаются программно. Их Text свойства содержат измененный адрес электронной почты и комментарий.
Чтобы обновить учетную запись пользователя с помощью API членства, необходимо сначала получить сведения о пользователе, которые мы делаем с помощью вызова Membership.GetUser(userName). Затем возвращаемый объект MembershipUser, свойства Email и Comment обновляются значениями, введенными в два текстовых поля интерфейса редактирования. Наконец, эти изменения сохраняются вызовом Membership.UpdateUser. Обработчик RowUpdating событий завершается возвратом GridView к его интерфейсу до редактирования.
Затем создайте RowDeleting обработчик событий Row Delete и добавьте следующий код:
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 значения из коллекции DataKeys GridView; затем это значение передается в метод 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, которые являются шаблонами, зависящими от ролей. Каждая группа ролей содержит одно свойство, Rolesуказывающее, к каким ролям применяется RoleGroup. Для Roles свойства можно задать одну роль (например, "Администраторы") или список ролей с разделителями-запятыми (например, "Администраторы, руководители").
Чтобы управлять группами ролей, щелкните ссылку "Изменить группы ролей" из смарт-тега элемента управления, чтобы открыть редактор коллекции RoleGroup. Добавьте две новые группы ролей. Задайте для первого свойства RoleGroup Roles значение "Администраторы", а второй — "Руководители".
Рис. 8. Управление шаблонами, специфичными для ролей, в LoginView с помощью редактора коллекции RoleGroup (щелкните, чтобы просмотреть изображение в полном размере)
Нажмите кнопку "ОК", чтобы закрыть редактор коллекции RoleGroup; Это обновляет декларативную разметку LoginView, чтобы включить <RoleGroups> раздел с дочерним элементом для каждой <asp:RoleGroup> роли, определенной в редакторе коллекции RoleGroup. Кроме того, раскрывающийся список "Представления" в смарт-теге LoginView, который изначально указан только как AnonymousTemplate и LoggedInTemplate, теперь также включает добавленные группы ролей.
Измените группы ролей, чтобы для пользователей в роли "Руководители" отображались инструкции по изменению учетных записей пользователей, а пользователи в роли "Администраторы" получали инструкции по редактированию и удалению. После внесения этих изменений декларативная разметка 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. Брюсу показано сообщение, специфичное для роли руководителя (Щелкните, чтобы просмотреть изображение полного размера)
Рис. 10. Tito отображается администраторам Role-Specific сообщение (щелкните, чтобы просмотреть изображение полного размера)
Как показано на снимках экрана на рисунках 9 и 10, LoginView отображает только один шаблон, даже если применяются несколько шаблонов. Брюс и Тито вошли в систему как пользователи, однако LoginView отображает только соответствующую группу ролей, а не LoggedInTemplate. Кроме того, Tito принадлежит как к ролям "Администраторы", так и к "Руководители", но элемент управления LoginView представляет шаблон, предусмотренный для роли "Администраторы", а не для "Руководители".
На рисунке 11 показан рабочий процесс, используемый элементом управления LoginView для определения шаблона для отрисовки. Обратите внимание, что если задано несколько ролей, шаблон LoginView отображает первую группу ролей, которая соответствует. Иными словами, если бы мы поместили группу ролей Supervisors как первую группу ролей, а администраторов как вторую, то когда Тито посещает эту страницу, он увидит сообщение Supervisors.
Рис. 11. Рабочий процесс элемента управления LoginView для определения шаблона для отрисовки (щелкните, чтобы просмотреть изображение полного размера)
Программное ограничение функциональных возможностей
Хотя элемент управления LoginView отображает различные инструкции на основе роли пользователя, посещающего страницу, кнопки редактирования и отмены остаются видимыми для всех. Нам нужно программно скрыть кнопки "Изменить и удалить" для анонимных посетителей и пользователей, которые не находятся ни в роли руководителей, ни администраторов. Нам нужно скрыть кнопку "Удалить" для всех, кто не является администратором. Для этого мы напишем немного кода, который программно ссылается на элементы управления CommandField, такие как кнопки редактирования и удаления (Edit и Delete LinkButtons), и при необходимости задает их свойства Visible на False.
Самый простой способ программно ссылаться на элементы управления в CommandField — сначала преобразовать его в шаблон. Для этого щелкните ссылку "Изменить столбцы" из смарт-тега GridView, выберите CommandField из списка текущих полей и щелкните ссылку "Преобразовать это поле в templateField". Это превращает CommandField в TemplateField с ItemTemplate и EditItemTemplate. Элемент ItemTemplate содержит кнопки-ссылки "Редактировать" и "Удалить", а EditItemTemplate содержит кнопки-ссылки "Обновить" и "Отмена".
Рис. 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 объект. По мере создания каждого объекта GridViewRow, возникает событие RowCreated. Чтобы скрыть кнопки "Изменить" и "Удалить" для несанкционированных пользователей, необходимо создать обработчик событий для этого события и программно ссылаться на элемент Edit and Delete 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, включая заголовок, нижний колонтитул, интерфейс пейджера и так далее. Мы хотим программно ссылаться на элемент Edit and Delete LinkButtons, если мы имеем дело с строкой данных, не в режиме редактирования (так как строка в режиме редактирования имеет кнопки "Обновить и отменить", а не "Изменить" и "Удалить"). Эта проверка обрабатывается инструкцией If .
Если мы имеем дело с строкой данных, которая не находится в режиме редактирования, ссылки на кнопки редактирования и удаления находятся, и их свойства Visible задаются на основе логических значений, возвращаемых методом объекта UserIsInRole(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. Применение правил авторизации на основе ролей к классам и методам
На шаге 2 мы ограничили возможности редактирования для пользователей в ролях наблюдателей и администраторов, а возможности удаления — только для администраторов. Это было сделано путем скрытия связанных элементов пользовательского интерфейса для несанкционированных пользователей с помощью программных методов. Такие меры не гарантируют, что несанкционированный пользователь не сможет выполнить привилегированное действие. Могут быть элементы пользовательского интерфейса, добавленные позже или которые мы забыли скрыть для несанкционированных пользователей. Или хакер может обнаружить другой способ получить страницу ASP.NET для выполнения требуемого метода.
Простой способ обеспечить недоступность определенной части функциональности для неавторизованного пользователя — это применить к этому классу или методу атрибутPrincipalPermission. Если среда выполнения .NET использует класс или выполняет один из его методов, она проверяет, имеет ли текущий контекст безопасности разрешение. Атрибут PrincipalPermission предоставляет механизм, с помощью которого можно определить эти правила.
Мы рассмотрели использование атрибута PrincipalPermission в руководстве по авторизации на основе пользователей. В частности, мы увидели, как оформить обработчики событий GridView SelectedIndexChanged и RowDeleting, чтобы их могли выполнять только аутентифицированные пользователи и Tito соответственно. Атрибут PrincipalPermission работает так же хорошо с ролями.
Давайте продемонстрируем использование атрибута PrincipalPermission в обработчиках событий RowUpdating и RowDeleting элемента GridView для запрета выполнения функций для неавторизованных пользователей. Все, что нам нужно сделать, — добавить соответствующий атрибут на вершину каждого определения функции:
<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 в начало класса code-behind в файле кода, чтобы импортировать это пространство имен.
Если, как-то, неадминистратор пытается выполнить RowDeleting обработчик событий или если нерабочий или неадминистратор пытается выполнить RowUpdating обработчик событий, среда выполнения .NET вызовет ошибку SecurityException.
Рис. 16. Если контекст безопасности не авторизован для выполнения метода, SecurityException генерируется исключение (щелкните, чтобы просмотреть изображение полного размера)
Помимо страниц ASP.NET, многие приложения также имеют архитектуру, которая включает различные слои, такие как бизнес-логика и уровни доступа к данным. Эти уровни обычно реализуются как библиотеки классов и предлагают классы и методы для выполнения бизнес-логики и связанных с данными функций. Этот PrincipalPermission атрибут полезен для применения правил авторизации к этим уровням.
Дополнительные сведения об использовании атрибута PrincipalPermission для определения правил авторизации для классов и методов см. в записи блога Скотта Гутри "Добавление правил авторизации в бизнес- и дата-уровни с помощью PrincipalPermissionAttributes".
Сводка
В этом руководстве мы рассмотрели, как определить грубые и детальные правила авторизации, основанные на ролях пользователя. Функция авторизации URL в ASP.NET позволяет разработчику страниц указать, какие удостоверения имеют или не имеют доступ к определенным страницам. Как мы узнали в руководстве по авторизации на основе пользователя, правила авторизации URL-адресов можно применять на базе конкретных пользователей. Они также могут применяться для каждой роли в отдельности, как мы видели на шаге 1 этого руководства.
Правила детальной авторизации могут применяться декларативно или программно. На шаге 2 мы рассмотрели использование функции RoleGroups элемента управления LoginView для отображения различных выходных данных на основе ролей пользователя. Мы также рассмотрели способы программного определения того, принадлежит ли пользователь определенной роли и как соответствующим образом настроить функциональные возможности страницы.
Счастливое программирование!
Дальнейшее чтение
Дополнительные сведения о разделах, описанных в этом руководстве, см. в следующих ресурсах:
-
Добавление правил авторизации к бизнес- и дата-слоям с использование
PrincipalPermissionAttributes - Изучение членства, ролей и профиля ASP.NET версии 2.0. Работа с ролями
- Список вопросов безопасности для ASP.NET 2.0
-
Техническая документация по элементу
<roleManager>
Сведения о авторе
Скотт Митчелл, автор нескольких книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга — Sams Teach Yourself ASP.NET 2.0 за 24 часа. С Скоттом можно связаться на mitchell@4guysfromrolla.com или через его блог http://ScottOnWriting.NET.
Особое спасибо...
Эта серия учебников была проверена многими полезными рецензентами. Ведущие рецензенты для этого руководства: Сучи Бандерджи и Тереса Мерфи. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com