Операционная система Windows, включая самые последние на сегодняшний день версии Windows XP/2003, содержит ряд уязвимостей, немедленно замечаемых заинтересованными лицами. В Интернете встречается программка PassView (http://www.nht-team.org/) — она позволяет узнавать пароли от многих программ, установленных на вашем компьютере, среди которых Internet Explorer, Outlook Express, ICQ. Программа в заархивированном виде занимает около 60 Кб и не требует установки, кроме всего прочего, она позволяет увидеть пароли от диалапа – для этого достаточно получить доступ с правами администратора системы на считанные секунды и запустить PassView (рис. 10.1).
(рис 10.1) Вкладка Ras программы PassView, на которую выводятся данные всех соединений данного пользователяПолучение пароля администратора в Windows 2000/XP/2003 тоже не слишком сложная задача. Рассмотрим один из способов — восстановление пароля из SAM-базы. Все логины и пароли учетных записей хранятся в зашифрованном виде в файле SAM, расположенном в каталоге C:\WINDOWS\System32\Config. При попытке открыть этот файл непосредственно, из операционной системы появляется сообщение системы о нарушении доступа к файлу. Дело в том, что все файлы, находящиеся в этой папке, представляют собой фрагменты реестра, постоянно используемые системой, поэтому доступ к ним возможен не из самой ОС. Для этого применяем любой файловый менеджер (обычно на мультизагрузочных компакт-дисках можно найти их, причем как для файловой системы FAT32, так и для NTFS) для просмотра содержимого жесткого диска в режиме DOS и копируем на любой съемный носитель два файла — SAM и SYSTEM, содержащей системный ключ Syskey. Теперь на другом компьютере нам осталось только расшифровать содержимое скопированных файлов. Запускаем программу SamInside (в заархивированном виде около 800 Кб, http://www.insidepro.com/) и импортируем сначала файл SAM, а затем SYSTEM. Программа SamInside поддерживает перебор паролей по двум алгоритмам — LM и NT, кроме того, предоставляет возможность для определения диапазона предполагаемого пароля, включая перебор по словарям (рис. 10.2).
(рис 10.2) Настройки подбора пароляВ качестве пароля на свою учетную запись я установил password12345, и на компьютере P IV 2.4 ГГц, 512 Мб RAM получил часть пароля по алгоритму LM всего за 15 минут. Даже подбор по алгоритму NT, с учетом параметров, указанных на рис. 10.2, занимает всего 2 часа (рис. 10.3).
(рис 10.3) Окно программы в режиме определения подбора пароля по алгоритму NTКонечно, подбор сложного пароля, содержащего в случайном порядке буквы, числа и символы, займет гораздо больше времени, но привлечение больших вычислительных мощностей позволит справиться и с этой задачей. Мы не будем рассматривать здесь применение криптографических алгоритмов, теорию цифровых подписей или социальную инженерию — для этого бы потребовалось отдельная книга. Пример с учетными записями Windows должен просто навести вас на мысль, что любой инструмент безопасности требует критического и внимательного отношения к себе. Не следует всецело полагаться на ответственность разработчиков .NET Framework — всякий раз, когда в своих приложениях вы будете использовать один из принципов, речь о которых пойдет ниже, старайтесь оценивать все возможные контрмеры, способствующие исключению несанкционированного доступа.
Модель безопасности .NET Framework основывается на безопасности доступа кода (Code Access Security,
Под безопасностью доступа кода подразумевается разрешение на использование защищенных данных вашим приложением только в том случае, когда у него есть соответствующие права доступа. В тот момент, когда ваше приложение отправляет запрос на использование того или иного ресурса, .NET Framework проверяет наличие соответствующих прав доступа, и если таковых не обнаружено, запрещает доступ.
Под ролевой безопасностью подразумевается использование встроенных ролей. В зависимости от роли работающего пользователя приложение использует те или иные ресурсы.
Основой определения разрешений для системы безопасности служит само приложение, его сборка и так называемое удостоверение. Удостоверение — это набор информации о сборке и ее производителе. Удостоверение может включать в себя:
Производитель сборки может также включить и другую информацию о сборке, которая будет учитываться в том случае, если политика безопасности компьютера настроена на использование частной информации.
Данные, описывающие информацию о сборке, различаются по надежности. Например, строгое имя или производитель имеют больший приоритет, потому что их тяжелее подделать, в сравнении с зоной сборки и адресом сайта. Предоставлять доступ на основе недостаточно надежных данных о сборке представляется опасным, но если совмещать все данные о сборке, то можно добиться неплохого результата.
Разрешения — это право на использование частных ресурсов пользователя. Например, среди действий, защищаемых разрешениями доступа, есть такие, как чтение и запись файла, доступ к данным операционной системы или доступ к данным, находящимся в локальной базе данных.
.NET Framework имеет много встроенных разрешений, автоматически проверяющих код и разрешения на него. В таблице 10.1 перечислены все встроенные разрешения.
| Класс, представляющий разрешение | Охраняемый ресурс |
|---|---|
DirectoryServicesPermission |
Служба каталогов |
DnsPermission |
Службы DNS (Domain Namespace Services) |
EnvironmentPermission |
Ресурсы операционной системы |
EventLogPermission |
Журнал событий |
FileDialogPermission |
Диалоги открытия, сохранения файлов в пользовательском интерфейсе приложения |
FileIOPermission |
Работа с файлами |
IsolatedStorgeFilePermission |
Изолированное хранилище файлов |
MessageQueuePermission |
Очереди сообщений |
OleDbPermission |
Базы данных, доступ к которым осуществляется средствами поставщика данных OleDB |
PerformanceCounterPermission |
Счетчики производительности |
PrintingPermission |
Печать |
ReflectionPermission |
Информация о типах во время выполнения |
RegistryPermission |
Реестр |
SecurityPermission |
Работа с правами доступа |
ServiceControllerPermission |
Запуск или остановка Windows-служб |
SocketPermission |
Соединение с компьютером через порты |
SqlClientPermission |
Базы данных, доступ к которым осуществляется средствами SqlClient провайдера |
UIPermission |
Пользовательский интерфейс |
WebPermission |
Соединение с компьютерами через web-интерфейс |
В дополнение к встроенным разрешениям вы можете создать собственные, основываясь на классе CustomPermission.
.NET Framework принудительно налагает разрешения на управляемый код. Каждая загружаемая сборка получает набор разрешений на доступ к различным ресурсам. Эти разрешения основаны на политике безопасности. В свою очередь, политика безопасности использует удостоверение сборки для определения набора разрешений. Другими словами, .NET Framework применяет политику безопасности компьютера для назначения набора разрешений.
Группа кода состоит из
Сборка получает общий набор разрешений от всех членств, к которым она принадлежит.
Существует несколько уровней безопасности: сеть, компьютер, пользователь,
| Уровень безопасности | Описание |
|---|---|
| Сеть ( |
Уровень контролируется администратором сети и содержит группы кода, которые могут быть назначены любому управляемому коду во всей корпоративной сети |
| Компьютер (Machine) | Уровень контролируется администратором компьютера и содержит группы кода, которые могут быть назначены любому управляемому коду, выполняемому на данном компьютере |
| Пользователь (User) | Уровень контролируется пользователем компьютера и содержит группы кода, которые могут быть назначены любому управляемому коду, выполняемому от имени данного пользователя |
| Дополнительный уровень, позволяющий изолировать, выгружать и ограничивать выполняемый управляемый код |
Окончательный набор разрешений получается в результате суммы всех разрешений от каждой сборки, т.к. каждая сборка может иметь свой набор разрешений (рис. 10.4).
(рис 10.4) Принцип работы безопасности доступа кодаСреда выполнения Common Language Runtime (CLR) также имеет стандартную политику безопасности и использует ее набор разрешений ( табл. 10.3).
| Отсутствие разрешений (Nothing) | Отсутствуют какие-либо разрешения. Код не может быть запущен. Код из "небезопасной" зоны использует эту политику безопасности. |
| Извлечение (Execution) | Предоставляет разрешение только на запуск приложения. Не разрешает приложению использовать какие-либо ресурсы |
| Интернет (Internet) | Предоставляет разрешения на выполнение кода, открытие диалоговых окон, создание web-соединений с сайтом, с которого установлено приложение, и использование изолированного хранилища с ограниченным размером. По умолчанию, весь код из Интернета и "небезопасные" зоны используют эту политику безопасности |
| Корпоративная интрасеть (Local Intranet) | Предоставляет разрешения на запуск кода; его выполнение без ограничений; использование изолированного хранилища без лимита; использование DNS-служб; использование таких данных, как имя пользователя и временных переменных; создание web-соединения с сайтами, с которых взяты сборки, используемые в приложении; чтение и сохранение файлов из того же каталога, что и приложение. По умолчанию, весь код из сети использует эту политику безопасности |
| Весь доступ (Everything) | Предоставляет все стандартные разрешения, кроме отмены верификации |
| Полный доступ (Full Trust) | Предоставляет полный доступ ко всем ресурсам. По умолчанию, весь управляемый код локального компьютера использует эту политику безопасности |
Каждое приложение, которое использует CLR, должно взаимодействовать с системой безопасности во время выполнения. Когда приложение выполняется, CLR автоматически проверяет все сборки приложения и возвращает набор разрешений. В зависимости от набора разрешений или ресурсов, с которыми работает приложение, оно либо выполняет заданные действия, либо генерирует исключение безопасности.
Настройки безопасности каждого компьютера индивидуальны. Они меняются в зависимости от пользователя, администратора компьютера или администратора сети, и нет никакой гарантии, что ваше приложение получит необходимые разрешения на выполнение операций.
Взаимодействие с системой безопасности во время выполнения осуществляется двумя способами: императивным и декларативным. Декларативный способ взаимодействия осуществляется с помощью атрибутов, императивный – с помощью объектов классов в вашем приложении. Некоторые операции могут быть выполнены с помощью одного из способов, а некоторые — любым из них.
Из-за того, что большинство разрешений уже встроены в .NET Framework, практически не приходится создавать собственные разрешения. Выполняемый код, взаимодействующий с определенными ресурсами, проходит проверку и получает разрешения на использование данных ресурсов. Итак, основной задачей на стадии разработки остается лишь документирование необходимых приложению разрешений для того, чтобы быть уверенным, что приложение их получит. Это делается с помощью запроса разрешений и утилит настройки политики безопасности.
Для того чтобы добавить запрос на определенное разрешение, необходимо добавить соответствующий атрибут в файл ApplicationInfo.cs проекта. При компиляции сборки эти атрибуты сохранятся в метаданных. Запросы на разрешения проверяются CLR во время загрузки сборки. Существует три вида запросов, описание которых приводится в таблице 10.4.
| Запрос | Описание запроса |
|---|---|
| Запрос минимального набора разрешений (RequestMinimum) | Запрашивает необходимый минимум для корректной работы приложения. Если это разрешение не назначено политикой безопасности, то CLR не выполнит операции, а сгенерирует исключение типа PolicyException. Этим запросом необходимо пользоваться только для минимальных разрешений |
| Запрос дополнительного набора разрешений (RequestOptional) | Представляет набор разрешений, с которыми может работать ваше приложение, но без которых оно сможет корректно выполнять действия. Этим запросом описывают дополнительную функциональность приложения. Если это разрешение не назначено политикой безопасности, то приложение будет продолжать работать |
| Запрос на отказ от разрешений (RequestRefused) | Указываются разрешения, которые ваше приложение использовать не будет. Запросы указываются для обеспечения дополнительной безопасности |
В лекции 2 мы работали с проектом BinaryReadWrite. Скопируйте всю папку проекта и переименуйте ее в Permissions. Снова поместите в папку bin\Debug какой-нибудь файл формата MP3 и назовите его music.mp3. Запустите проект и убедитесь в том, что в этой же папке появился записанный файл newmusic.mp3. Теперь добавляем пространство имен System.Security и соответствующий блок исключений:
using System;
using System.IO;
using System.Security;
namespace BinaryReadWrite
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
//Добавляем блок обработки исключений
try
{
//Создаем новый поток Chtenie и указываем файл для чтения — music.mp3
FileStream Chtenie = new FileStream("music.mp3", FileMode.Open);
//Создаем экземпляр br класса BinaryReader и связываем его с потоком Chtenie
BinaryReader br = new BinaryReader(Chtenie);
// Создаем массив типа байтов и
//передаем ему размер в байтах — (например, 1000000 ) записываемого файла
byte[] n = br.ReadBytes(1000000);
//Закрываем поток
br.Close();
//Создаем новый поток Zapis и указываем название файла для записи sozdanajamuzika.mp3
FileStream Zapis = new FileStream("newmusic.mp3", FileMode.CreateNew, FileAccess.Write);
//Создаем экземпляр wr класса BinaryWriter и связываем его с потоком Zapis
BinaryWriter wr = new BinaryWriter(Zapis);
// Можно связывать объект wr с потоком, создавая его без названия:
//BinaryWriter wr = new BinaryWriter(new FileStream("sozdanajamuzika.mp3", FileMode.CreateNew, FileAccess.Write));
//Передаем в поток массив байтов n
wr.Write(n);
//Закрываем поток
br.Close();
}
catch (SecurityException ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
В окне Solution Explorer дважды щелкаем на файл AssemblyInfo.cs и для использования атрибутов добавляем пространство имен System.Security.Permissions:
using System.Reflection; using System.Runtime.CompilerServices; using System.Security.Permissions;
В конце листинга AssemblyInfo.cs определяем минимальный набор разрешений:
// Запрос минимального набора разрешений [assembly: UIPermission(SecurityAction.RequestOptional, Window = UIPermissionWindow.SafeTopLevelWindows)] [assembly: PermissionSet(SecurityAction.RequestOptional, Name = "LocalIntranet")]
Это разрешение не было назначено политикой безопасности, поэтому генерируется исключение (рис. 10.5).
(рис 10.5) Сгенерированное исключение при определении минимального набора разрешенийДля получения доступа следует определять другие наборы разрешений и настраивать политику безопасности. Например, данный запрос разрешений указывает, что сборка не нуждается в дополнительных разрешениях:
[assembly:PermissionSet(SecurityAction.RequestOptional, Unrestricted = false)]
На диске, прилагаемом к книге, вы найдете приложение Permissions (Code\Glava10\ Permissions).
С помощью утилиты Microsoft .NET Framework 1.1 Configuration можно настраивать политику безопасности на компьютере. Для ее запуска переходим в Мой компьютер\Панель управления\Администрирование\Microsoft .NET Framework 1.1 Configuration. Открываем вкладку Runtime Security Policy (рис. 10.6).
(рис 10.6) Утилита Microsoft .NET Framework 1.1 ConfigurationОкно программы разделено на две части. В левой мы можем выбирать нужный объект, в правой немедленно выводится панель информации с описанием этого объекта и ссылками на возможные действия. Для каждого уровня безопасности — Enterprise, Machine и User (описание см. в табл. 10.2) — имеется своя настройка трех категорий — Code Groups, Permission Sets и Policy Assemblies. Поскольку сами категории совершенно одинаковы для всех уровней, выберем один уровень — Machine — и рассмотрим настройку доступа. Переходим на вкладку Machine —> Code Groups —> All Code —> My_Computer_Zone. На панели описания мы видим описание данной группы кода и ее свойства — условия членства в группе и набор разрешений для данной группы (рис. 10.7).
(рис 10.7) Свойства группы My_Computer_ZoneВ этой группе кода определяется доступ на локальном компьютере, аналогично на вкладках LocalIntranet_Zone и Internet_Zone — доступ внутри локальной сети и в Интернете. Если настройка доступа для групп кода My_Computer_Zone и LocalIntranet_Zone представляет ограниченный интерес, то изменение свойств Internet_Zone требуется гораздо чаще — ведь большинство деструктивных действий, совершаемых без ведома пользователя, осуществляется через Интернет. Щелкнем правой кнопкой мыши на группе Internet_Zone и в появившемся контекстном меню выберем "Свойства". На вкладке Membership Condition по умолчанию определено
(рис 10.8) Условие вступления Strong Name в членство группы Internet_ZoneПосле введения необходимых данных переключаемся на вкладку Permission Set, где определяем набор разрешений для этой группы. После завершения редактирования и закрытия окна свойств изменения вступят в силу. Для создания собственного набора разрешений щелкаем правой кнопкой на вкладке Permission Sets и выбираем пункт меню New (рис. 10.9).
(рис 10.9) Создание набора разрешений Permission SetsВ появившемся окне вводим имя нового набора разрешений и его описание (рис. 10.10).
(рис 10.10) Создание нового набора разрешенийВ следующем окне выбираем набор из предлагаемого списка разрешений (рис. 10.11).
(рис 10.11) Добавление разрешений к новому наборуПри выборе каждого разрешения появляется окно дополнительных свойств, предлагающих более детальную настройку — например, доступ только для чтения, для чтения и записи или только записи. Созданный набор разрешений отображается в списке Permission Sets и может быть использован для настройки политики безопасности учетной записи, компьютера или корпоративной сети.
Утилита командной строки caspol.exe, поставляемая с .NET Framework, позволяет просматривать и манипулировать политикой безопасности. Файл утилиты располагается в каталоге С\WINDOWS\Microsoft.NET\Framework\ v1.1.4322\ CasPol.exe.
Утилита caspol.exe отображает всю доступную информацию об уровне безопасности, установленном по умолчанию (Machine). Эта информация включает в себя иерархию групп кода, название набора разрешений и имена сборок, использующих политику Full Trust.
Утилита caspol.exe имеет следующий синтаксис:
caspol <параметры> <аргументы>
В таблице 10.5 приводится список некоторых параметров утилиты.
| Параметр | Действие | Примечания |
|---|---|---|
-l |
Список (List) | Вывод всей доступной информации об уровне безопасности, установленном по умолчанию |
-lg |
Список групп кода (List Group Code) | Вывод списка групп кода уровня безопасности, установленного по умолчанию |
-lp |
Список наборов разрешений (List Permission Set) | Вывод списка наборов разрешений |
-ag |
Добавление группы кода (Add Code Group) | Добавление новой группы кода в уровень безопасности |
-url |
URL | Использование сборкой URL в качестве условия принадлежности к группе |
| -n | Название группы кода (Code Group Name) | Задание имени для новой группы кода |
-exclusive |
Установка флага Exclusive для уровня безопасности | Используется для тестирования. Любая сборка, встретившая условие группы кода, получает соответствующий набор разрешений. Например, это может применяться для предотвращения автоматического назначения набора разрешений Full Trust |
-cg |
Поменять группу кода (Change Code Group) | Изменение существующей группы кода |
-rg |
Удалить группу кода (Remove Code Group) | Удаление существующей группы кода |
Для работы с утилитой выбираем Пуск\Все программы\ Microsoft Visual Studio .NET 2003 \ Visual Studio .NET Tools \ Visual Studio .NET 2003 Command Prompt и в запустившейся командной строке вводим
caspol.exe
Появляется справка утилиты, которую затем можно снова вывести одной из команд
caspol –help caspol -? caspol /?
Для вывода информации о группах кода и наборов разрешений вводим
caspol -l
Для получения списка групп кода вводим
caspol -lg
Для просмотра списка наборов разрешений вводим
caspol –lp
Для сброса всех пользовательских настроек вводим
caspol –reset
По умолчанию весь код, запускающийся на локальной машине, получает набор разрешений полного доступа Full Trust. Нам может потребоваться, для тестовых целей, чтобы код выполнялся под другим набором разрешений. Самый простой способ сделать это — создать папку и настроить политику безопасности для причисления этой папки к другой зоне. Это делается следующим образом.
Создаем папку C:\testpermissions, затем в командной строке создаем новую группу разрешений:
сaspol –ag 1 –url file:///C:/testfolder/ * Internet -n test_permissions_group –exclusive on
Этот код добавляет новую группу кода test_permissions_group, условием вступления в которую является запуск из папки testpermissions. После этого копируем приложение в папку testpermissions и запускаем его с набором разрешений Internet.
Изменяем набор разрешений на Execution командой
Caspol –cg Test_Group Execution
Удаляем тестовую группу командой
Caspol –rg Test_Group
Ролевая модель безопасности использует данные пользователя в качестве источника информации. Как правило, эта информация состоит из имени и роли пользователя.
Ролевая модель безопасности в .NET Framework использует две концепции: личность (identity), представляющее собой имя пользователя, и его роль (
.NET Framework позволяет разработчикам применять встроенный в Microsoft Windows набор ролей. Также вы можете задействовать и частный набор ролей.
Перед тем как использовать роль пользователя, нам необходимо проверить личность этого пользователя. Аутентификация — это обнаружение и проверка личности пользователя путем сравнения введенной пользователем информации с существующими данными. Например, при проверке электронной почты вы вводите имя пользователя и пароль. Сервер сверяет их со своими данными и, если они идентичны, открывает доступ к вашей почте.
Сегодня существует множество механизмов аутентификации пользователей. Некоторые из них могут использоваться в .NET Framework приложениях. В операционной системе Windows используется механизм Microsoft
После того как мы удостоверились в личности пользователя, он проходит авторизацию. Авторизация — это определение прав пользователя на выполнение тех или иных действий, после чего ему предоставляется возможность работы с определенными ресурсами.
Объекты "личность" и "роль" применяются для доступа к информации о пользователе. Мы можем использовать информацию об имени встроенной учетной записи в операционной системе Windows и роли этой учетной записи с помощью объектов WindowsIdentity и WindowsPrincipal. Для доступа к информации о пользователе, основанной на собственном механизме аутентификации, задействуются объекты GenericIdentity и GenericPrincipal.
Объект "личность" инкапсулирует такую информацию о пользователе, как имя и тип аутентификации. В библиотеке .NET Framework есть три типа объекта "личность" (таблица 10.6).
| Личность | Описание |
|---|---|
| Windows Identity | Представляет информацию о текущей учетной записи Windows. Windows Identity позволяет определять полномочия пользователя в ОС и использовать их в приложении. Реализуется классом WindowsIdentity |
| Generic Identity | Представляет информацию о пользователе, которая основана на частном механизме аутентификации, используемом в приложении. Реализуется классом GenericIdentity |
| Custom Identity | Представляет "личность", которая инкапсулирует частную информацию о пользователе. Реализуется классом, в котором имеется интерфейс IIdentity |
Все классы, описывающие "личность", должны реализовывать интерфейс IIdentity. Интерфейс IIdentity имеет три открытых свойства:
Name — имя пользователя.IsAuthenticated — логическая переменная, принимающая одно из двух значений в зависимости от результата прохождения пользователем аутентификации.AuthenticationType — тип аутентификации пользователя."Роль" определяет право пользователей на выполнение действий, причем возможно наличие нескольких пользователей (входящих в группу) с различными именами, но равными ролями. Практически такая ситуация характерна для информационных систем — например, в банке права менеджера и оператора различаются, причем может быть несколько как менеджеров, так и операторов.
В библиотеке .NET Framework есть три типа объекта "роль" (таблица 10.7).
| Роль | Описание |
|---|---|
| Windows |
Представляет пользователей операционной системы Windows и их роли. Реализуется классом WindowsPrincipal |
| Generic |
Представляет независимых от операционной системы пользователей и их роли. Реализуется классом GenericPrincipal |
| Custom |
Представляет специальную для приложения информацию о ролях. Реализуется классом, в котором имеется интерфейс IPrincipal |
Все классы, описывающие объект "роль", должны реализовывать интерфейс IPrincipal. Интерфейс IPrincipal имеет одно свойство — Identity, которое описывает объект класса, реализующего интерфейс Iidentity, и метод — IsInRole, который определяет, принадлежит ли текущий "принципал" к заданной роли.
Существует два способа создания объекта WindowsPrincipal. Разница между ними в том, что один способ проверяет введенную информацию один раз, а второй — постоянно.
Если однократной проверки роли достаточно, то создаем объект WindowsPrincipal следующим образом:
// Инициализируем объект класса WindowsIdentity
// с помощью статического метода WindowsIdentity.GetCurrent()
WindowsIdentity identity = WindowsIdentity.GetCurrent();
// Создаем новый объект класса WindowsPrincipal
// и передаем в качестве параметра объект identity
WindowsPrincipal principal = new WindowsPrincipal(identity);
Если же вам необходимо проверять роль пользователя постоянно, то лучше создать объект WindowsPrincipal следующим образом:
// Указываем домену приложения политику ролевой безопасности // с помощью члена WindowsPrincipal перечисления PrincipalPolicy, // переданного в статический метод AppDomain.CurrentDomain. // SetPrincipalPolicy. AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); // После того как новая политика установлена, вы можете использовать // объект из статического свойства CurrentPrincipal класса Thread. WindowsPrincipal principal = System.Threading.Thread.CurrentPrincipal as WindowsPrincipal;
Создайте новое консольное приложение и назовите его WindowsIdentityandPrincipal. Далее привожу листинг с комментариями:
using System;
//Подключаем пространства имен Threading и Principal
using System.Threading;
using System.Security.Principal;
namespace WindowsIdentityandPrincipal
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
// Создаем и инициализируем объекты
// WindowsPrincipal и WindowsIdentity
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
// Выводим информацию, содержащуюся в объекте класса WindowsPrincipal
Output("Name", principal.Identity.Name);
Output("Type", principal.Identity.AuthenticationType);
Output("Authenticated", principal.Identity.IsAuthenticated.ToString());
// Выводим информацию, содержащуюся в объекте класса WindowsIdentity
Output("IdentName", identity.Name);
Output("IdentType", identity.AuthenticationType);
Output("IdentIsAuthenticated", identity.IsAuthenticated.ToString());
Output("IsAnonymous", identity.IsAnonymous.ToString());
Output("IsGuest", identity.IsGuest.ToString());
Output("IsSystem", identity.IsSystem.ToString());
Output("Token", identity.Token.ToString());
}
//Метод Output выводит информацию на экран, причем myName — это название
//параметра, а myValue — его значение.
public static void Output(string myName, string myValue)
{
Console.WriteLine(myName + "= {0}", myValue);
}
В результате выводится полный список значений WindowsIdentity и WindowsPrincipal на данном компьютере (рис. 10.12).
(рис 10.12) Результат приложения WindowsIdentityandPrincipalНа диске, прилагаемом к книге, вы найдете приложение WindowsIdentityandPrincipal (Code\Glava10\ WindowsIdentityandPrincipal).
Классы GenericPrincipal совместно с классом GenericIdentity применяются для реализации ролевой безопасности, не зависящей от системы безопасности Windows. Например, у пользователя запрашиваются имя и пароль, которые затем сверяются с данными, хранящимися в приложении или локальной базе данных, и, после успешной проверки, создаются объекты GenericPrincipal и GenericIdentity на основании введенных значений.
Для реализации ролевой безопасности на основе объектов классов GenericPrincipal и GenericIdentity необходимо выполнить следующие действия:
GenericIdentity и инициализировать его именем пользователя;GenericPrincipal и инициализировать его только что созданным объектом класса GenericIdentity и строковым массивом, содержащим роли пользователя;GenericPrincipal к текущему потоку. Прикрепление к текущему потоку необходимо для осуществления последующей проверки полномочий пользователя в коде приложения.Синтаксис этих действий следующий:
// Создаем и инициализируем именем пользователя
// объект класса GenericIdentity
GenericIdentity identity = new GenericIdentity("Username");
// Создаем строковый массив, содержащий роли пользователя.
string[] userRoles = new string[]{"Administrator", "PowerUser", "GuestUser"};
// Создаем объект класса GenericPrincipal и инициализируем его
// объектом класса GenericIdentity identity и строковым массивом с
// ролями.
GenericPrincipal principal = new GenericPrincipal(identity, userRoles);
// Прикрепляем объект роли к текущему процессу.
// Этот и все дочерние процессы будут иметь данную роль.
Thread.CurrentPrincipal = principal;
Создайте новое консольное приложение и назовите его GenericIdentityandPrincipal . Далее привожу листинг с комментариями:
using System;
using System.Threading;
using System.Security.Principal;
namespace GenericIdentityandPrincipal
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
[STAThread]
static void Main(string[] args)
{
// Создаем и прикрепляем объект роли.
CreateGenericPrincipalAndIdentity(Roles.PowerUser);
// Вводим один из способов осуществления проверки роли.
if(Thread.CurrentPrincipal.IsInRole("User"))
{
Console.WriteLine("У вас нет прав для работы с этим приложением");
}
else if(Thread.CurrentPrincipal.IsInRole("PowerUser"))
{
Console.WriteLine("Добро пожаловать, {0}. Ваша роль — Продвинутый пользователь", Thread.CurrentPrincipal.Identity.Name);
}
else // Administrator
{
Console.WriteLine("Добро пожаловать, {0}. Ваша роль — Администратор", Thread.CurrentPrincipal.Identity.Name);
}
}
// Создание объектов личности и роли
static void CreateGenericPrincipalAndIdentity(Roles role)
{
// Создаем и инициализируем именем пользователя
// объект класса GenericIdentity
GenericIdentity identity = new GenericIdentity("Username");
// Создаем строковый массив, содержащий роли пользователя.
string[] userRoles = new string[]{role.ToString()};
// Создаем объект класса GenericPrincipal и инициализируем его
// объектом класса GenericIdentity identity и строковым массивом с ролями.
GenericPrincipal principal = new GenericPrincipal(identity, userRoles);
// Прикрепляем объект роли к текущему процессу.
// Этот и все дочерние процессы будут иметь данную роль.
Thread.CurrentPrincipal = principal;
}
enum Roles
{
Administrator,
PowerUser,
User
}
}
}
Если пользователь проходит проверку, он получает дальнейший доступ (рис. 10.13).
(рис 10.13) Результат приложения GenericIdentityandPrincipalНа диске, прилагаемом к книге, вы найдете приложение GenericIdentityandPrincipal (Code\Glava10\ GenericIdentityandPrincipal).
После создания объекта роли к нему можно добавить код для реализации контроля доступа, основанного на ролях пользователей. Рассмотрим пример, в котором осуществляется контроль доступа по имени пользователя, причем сравнение строк не чувствительно к регистру. Создайте новое консольное приложение и назовите его AccessControl. Далее привожу листинг с комментариями:
using System;
using System.Threading;
using System.Security.Principal;
namespace AccessControl
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
GenericIdentity identity = new GenericIdentity("MANAGER");
string[] userRoles = new string[]{"Administrator"};
GenericPrincipal principal = new GenericPrincipal(identity, userRoles);
Thread.CurrentPrincipal = principal;
ValidateUserName();
ValidateRole();
}
//Проверяем пользователя по личности
static void ValidateUserName()
{
if (String.Compare(Thread.CurrentPrincipal.Identity.Name, "manager", true) == 0)
{
Console.WriteLine("Добро пожаловать, пользователь Manager");
}
else
{
throw new System.Security.SecurityException("У вас нет прав для выполнения текущей операции");
}
}
//Проверяем пользователя по роли
static void ValidateRole()
{
if (Thread.CurrentPrincipal.IsInRole("Administrator"))
{
Console.WriteLine("Добро пожаловать, пользователь Manager");
}
else
{
throw new System.Security.SecurityException("У вас нет прав для выполнения текущей операции");
}
}
}
}
При запуске этого приложения дважды осуществляется авторизация – по имени пользователя (личности) и его роли (рис. 10.14):
(рис 10.14) Результат работы приложения AccessControlДля объектов класса WindowsIdentity имя пользователя представлено в виде имени пользователя и домена. Например, если бы приведенный выше пример использовал объект класса WindowsIdentity, то имя для сравнения было бы следующим: DOMAIN\Manager.
Для объектов класса WindowsPrincipal роль включает в себя и имя домена. Ниже представлен пример проверки роль объектом класса WindowsPrincipal:
if(WindowsPrincipalObj.IsInRole("DOMAIN\\ Manager "))
// Разрешить действие
Если учесть, что ваше приложение может кочевать по сети в поисках нового пользователя, то строго прописывать имя домена не рекомендуется. Вместо этого для проверки ролей пользователей объектом класса WindowsPrincipal нужно указывать не строку с названием роли, а член перечисления WindowsBuiltInRole, как показано ниже:
MyPrincipal.IsInRole(WindowsBuiltInRole. Manager);
На диске, прилагаемом к книге, вы найдете приложение AccessControl (Code\Glava10\ AccessControl).
Мы обсудили теоретические основы безопасности .NET Framework, займемся теперь практическим использованием этих концепций. Создайте новое Windows-приложение и назовите его CustomSecurity. При запуске приложение будет требовать авторизацию пользователя и, в зависимости от его роли, предоставлять разные права доступа. В главной форме будет отображаться список всех пользователей и предоставляться возможность изменять роль пользователей. Список пользователей будет содержаться в файле Users.xml, для его создания в окне щелкаем правой кнопкой мыши в окне Solution Explorer и выбираем Add/Add New Item… . В появившемся окне выбираем XML File. Создадим нескольких пользователей, относящихся к трем группам — admin, manager, operator:
<?xml version="1.0" encoding="utf-8" ?>
<users>
<user name="admin" id="1" role="admin" />
<user name="manager" id="2" role="manager" />
<user name="accountant" id="3" role="accountant" />
<user name="student1" id="4" role="operator" />
<user name="student2" id="5" role="operator" />
<user name="student3" id="6" role="operator" />
</users>
Для просмотра и редактирования данных в виде таблицы щелкните на кнопке
, расположенной внизу на панели. Закончив редактирование, скопируйте файл Users.xml из папки проекта в папку bin/Debug.
Вынесем логику определения личности и роли пользователей в отдельные классы — CustomIdentity.cs и CustomPrincipal.cs. Для добавления в проект отдельных классов щелкаем в окне Solution Explorer правой кнопкой и выбираем Add/ Add Class… . Далее привожу листинг CustomIdentity.cs c комментариями:
using System;
using System.Security.Principal;
using System.Xml;
namespace CustomSecurity
{
/// <summary>
/// Класс CustomIdentity , описывающий "личность", наследует от класса IIdentity
/// </summary>
public class CustomIdentity : IIdentity
{
//Вводим переменные аутентификации
private bool _isAuth;
private string _name;
private string _authType;
private int _id;
/// <summary>
/// Конструктор.
/// </summary>
public CustomIdentity()
{
this._isAuth = false;
this._authType = String.Empty;
this._name = String.Empty;
this._id = -1;
}
/// <summary>
///Создаем конструктор, принимающий имя пользователя.
/// </summary>
/// <param name="userName">Имя пользователя.</param>
public CustomIdentity(string userName)
{
this._id = this.AuthUserName(userName);
this._name = userName;
this._isAuth = true;
this._authType = "Частный тип аутентификации.";
}
/// <summary>
/// Определяем уникальный идентификатор пользователя.
/// </summary>
public int ID
{
get { return this._id; }
}
#region IIdentity Members
/// <summary>
/// Проверка аутентификации пользователя.
/// </summary>
public bool IsAuthenticated
{
get
{
// Реализуем свойство интерфейса.
return this._isAuth;
}
}
/// <summary>
/// Определяем имя пользователя.
/// </summary>
public string Name
{
get
{
// Реализуем свойство интерфейса.
return this._name;
}
}
/// <summary>
/// Определяем тип аутентификации.
/// </summary>
public string AuthenticationType
{
get
{
// Реализуем свойство интерфейса.
return this._authType;
}
}
#endregion
/// <summary>
/// Проверяем, существует ли имя пользователя в базе данных — файле XML.
/// </summary>
/// <param name="name">Имя пользователя.</param>
/// <returns>ID пользователя.</returns>
private int AuthUserName(string name)
{
// Считываем и сравниваем имя пользователя.
XmlTextReader xmlReader = new XmlTextReader("Users.xml");
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
while(xmlReader.Read())
{
if(xmlReader["name"] == name)
return Int32.Parse(xmlReader["id"]);
}
// Если пользователь не найден, генерируем исключение.
throw new System.Security.SecurityException(String.Format("Пользователь {0} не найден в базе данных.", name));
}
}
}
Листинг CustomPrincipal.cs:
using System;
using System.Security.Principal;
using System.Xml;
namespace CustomSecurity
{
/// <summary>
/// Класс CustomPrincipal, описывающий роль, наследует от класса IPrincipal
/// </summary>
public class CustomPrincipal :IPrincipal
{
private CustomIdentity _indentity;
private string _role;
/// <summary>
/// Конструктор.
/// </summary>
/// <param name="identity">Определяем личность пользователя.</param>
public CustomPrincipal(CustomIdentity identity)
{
// Инициализируем личность
this._indentity = identity;
// Инициализируем переменную только один раз. Если роль изменится в процессе выполнения приложения, то
// изменения вступят в силу только после перезагрузки приложения.
this._role = this.GetUserRole();
}
#region IPrincipal Members
/// <summary>
/// Свойство личности пользователя.
/// </summary>
public IIdentity Identity
{
get
{
// Реализуем свойство интерфейса.
return this._indentity;
}
}
/// <summary>
/// Проверяем, прнадлежит ли пользователь к заданной роли.
/// </summary>
/// <param name="role">Роль.</param>
/// <returns></returns>
public bool IsInRole(string role)
{
// Реализуем метод интерфейса.
return role == this._role;
// Если необходимо реагировать на изменение роли без перезагрузки приложения, то это можно сделать так:
//return role == this.GetUserRole();
}
#endregion
/// <summary>
/// Возвращаем роль пользователя.
/// </summary>
/// <returns></returns>
private string GetUserRole()
{
// Считываем и сравниваем имя пользователя.
XmlTextReader xmlReader = new XmlTextReader("Users.xml");
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
while(xmlReader.Read())
{
if(xmlReader["name"] == this._indentity.Name)
return xmlReader["role"];
}
// Если роль пользователя не найдена, генерируем исключение.
throw new System.Security.SecurityException(String.Format("Роль пользователя {0} не найдена в базе данных.",
this._indentity.Name));
}
}
}
Теперь займемся пользовательским интерфейсом программы и привязкой обработчиков. Все приложения, которые мы рассматривали в предыдущих лекциях, я сопровождал описанием дизайна форм и не указывал в листинге область Windows Form Designer generated code. Сейчас я поступлю наоборот — я приведу код, генерируемый средой, и изображения формы, а вы, скопировав код, относящийся к дизайну, разберетесь с элементами, которые были использованы для создания интерфейса. Итак, главная форма программы будет иметь следующий вид (рис. 10.14):
(рис 10.15) Главная форма проекта CustomSecurityВ окне Solution Explorer переименуйте Form1.cs в MainForm.cs, добавьте на форму элемент управления ImageList и поместите в коллекцию две иконки (на рис. 10.15 эти иконки были использованы для кнопок панели инструментов).
Листинг формы MainForm:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Xml;
using System.Xml.XPath;
using System.IO;
namespace CustomSecurity
{
/// <summary>
/// Главная форма.
/// </summary>
public class MainForm : System.Windows.Forms.Form
{
private System.Windows.Forms.ToolBarButton btnViewUsers;
private System.Windows.Forms.ToolBarButton btnEditUsers;
private System.Windows.Forms.ImageList toolBoxIcons;
private System.Windows.Forms.ToolBar toolBar;
private System.Windows.Forms.ListView lstViewUsers;
private System.Windows.Forms.ColumnHeader colUserName;
private System.Windows.Forms.ColumnHeader colUserRole;
private System.Windows.Forms.ColumnHeader colUserId;
private System.ComponentModel.IContainer components;
public MainForm()
{
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support — do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(MainForm));
this.toolBar = new System.Windows.Forms.ToolBar();
this.btnViewUsers = new System.Windows.Forms.ToolBarButton();
this.btnEditUsers = new System.Windows.Forms.ToolBarButton();
this.toolBoxIcons = new System.Windows.Forms.ImageList(this.components);
this.lstViewUsers = new System.Windows.Forms.ListView();
this.colUserName = new System.Windows.Forms.ColumnHeader();
this.colUserRole = new System.Windows.Forms.ColumnHeader();
this.colUserId = new System.Windows.Forms.ColumnHeader();
this.SuspendLayout();
//
// toolBar
//
this.toolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {
this.btnViewUsers,
this.btnEditUsers});
this.toolBar.DropDownArrows = true;
this.toolBar.ImageList = this.toolBoxIcons;
this.toolBar.Location = new System.Drawing.Point(0, 0);
this.toolBar.Name = "toolBar";
this.toolBar.ShowToolTips = true;
this.toolBar.Size = new System.Drawing.Size(424, 28);
this.toolBar.TabIndex = 0;
this.toolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBar_ButtonClick);
//
// btnViewUsers
//
this.btnViewUsers.ImageIndex = 0;
this.btnViewUsers.Tag = "view";
this.btnViewUsers.ToolTipText = "Вывести список пользователей";
//
// btnEditUsers
//
this.btnEditUsers.ImageIndex = 1;
this.btnEditUsers.Tag = "edit";
this.btnEditUsers.ToolTipText = "Редактировать роль пользователя";
//
// toolBoxIcons
//
this.toolBoxIcons.ImageSize = new System.Drawing.Size(16, 16);
this.toolBoxIcons.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("toolBoxIcons.ImageStream")));
this.toolBoxIcons.TransparentColor = System.Drawing.Color.Transparent;
//
// lstViewUsers
//
this.lstViewUsers.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.colUserName,
this.colUserRole,
this.colUserId});
this.lstViewUsers.Cursor = System.Windows.Forms.Cursors.Hand;
this.lstViewUsers.Dock = System.Windows.Forms.DockStyle.Fill;
this.lstViewUsers.FullRowSelect = true;
this.lstViewUsers.GridLines = true;
this.lstViewUsers.Location = new System.Drawing.Point(0, 28);
this.lstViewUsers.MultiSelect = false;
this.lstViewUsers.Name = "lstViewUsers";
this.lstViewUsers.Size = new System.Drawing.Size(424, 238);
this.lstViewUsers.SmallImageList = this.toolBoxIcons;
this.lstViewUsers.TabIndex = 1;
this.lstViewUsers.View = System.Windows.Forms.View.Details;
//
// colUserName
//
this.colUserName.Text = "Имя пользователя";
this.colUserName.Width = 125;
//
// colUserRole
//
this.colUserRole.Text = "Роль пользователя";
this.colUserRole.Width = 125;
//
// colUserId
//
this.colUserId.Text = "Уникальный идентификатор";
this.colUserId.Width = 175;
//
// MainForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(424, 266);
this.Controls.Add(this.lstViewUsers);
this.Controls.Add(this.toolBar);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "MainForm";
this.Text = "Ролевая безопасность";
this.Load += new System.EventHandler(this.MainForm_Load);
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}
private void MainForm_Load(object sender, System.EventArgs e)
{
// Получаем имя пользователя.
LogInForm login = new LogInForm();
if(login.ShowDialog() != DialogResult.OK)
{
// Если пользователь отменил ввод имени, закрываем приложение
this.Close();
return;
}
try
{
// Создаем объекты IIdentity и IPrincipal
CustomIdentity identity = new CustomIdentity(login.txbUserName.Text);
CustomPrincipal principal = new CustomPrincipal(identity);
Thread.CurrentPrincipal = principal;
this.ValidateUser();
}
catch(Exception ex)
{
// Обрабатываем исключения и закрываем приложение.
MessageBox.Show("Во время выполнения приложения возникла ошибка: "+ex.Message, "Ошибка");
this.Close();
return;
}
}
/// <summary>
/// Изменяем пользовательский интерфейс в зависимости от роли пользователя.
/// </summary>
private void ValidateUser()
{
CustomPrincipal principal = Thread.CurrentPrincipal as CustomPrincipal;
if(principal.IsInRole("operator"))
{
// Если роль пользователя — operator, скрываем функциональность.
btnEditUsers.Enabled = false;
btnViewUsers.Enabled = false;
}
if(principal.IsInRole("manager"))
{
// Для роли пользователя manager скрываем возможности изменения роли пользователя.
btnEditUsers.Enabled = false;
}
}
private void toolBar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
// Проверяем, какая кнопка была нажата, и
// выполняем соответствующее действие.
switch(e.Button.Tag.ToString())
{
case "view":
this.ViewUsers();
break;
case "edit":
this.EditUser();
break;
}
}
/// <summary>
/// Выводим пользователей на экран.
/// </summary>
private void ViewUsers()
{
lstViewUsers.Items.Clear();
XmlTextReader xmlReader = new XmlTextReader("Users.xml");
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
while(xmlReader.Read())
{
// Считываем данные из XML-файла, представляем их в виде
// строки элемента управления ListView
if(xmlReader["name"] == null)
continue;
ListViewItem item = new ListViewItem(new
string[]{xmlReader["name"], xmlReader["role"], xmlReader["id"]}, 0);
lstViewUsers.Items.Add(item);
}
xmlReader.Close();
}
/// <summary>
/// Изменяем выбранного пользователя.
/// </summary>
private void EditUser()
{
if(lstViewUsers.SelectedItems.Count == 0)
{
MessageBox.Show("Вы дожны выбрать пользователя.", "Сообщение приложения");
}
else
{
string userName = lstViewUsers.SelectedItems[0].Text;
EditUserForm edit = new EditUserForm(userName);
if(edit.ShowDialog() != DialogResult.OK)
return;
// Считываем новую роль.
string newRole = edit.cmbRole.SelectedItem.ToString();
// Обновляем файл XML новыми данными.
XmlDocument doc = new XmlDocument();
StreamReader reader = new StreamReader("Users.xml");
doc.LoadXml(reader.ReadToEnd());
reader.Close();
XmlElement root = doc.DocumentElement;
foreach(XmlNode child in root.ChildNodes)
{
if(child.Attributes["name"].Value == userName)
{
child.Attributes["role"].Value = newRole;
break;
}
}
doc.Save("Users.xml");
this.ViewUsers();
}
}
}
}
При запуске приложения доступ к главной форме будет блокироваться до прохождения авторизации (рис.рис. 10.16).
(рис 10.16) Авторизация пользователяДобавьте новую Windows-форму в проект и назовите ее LoginForm.cs. Листинг этой формы:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace CustomSecurity
{
/// <summary>
/// Форма ввода имени пользователя.
/// </summary>
public class LogInForm : System.Windows.Forms.Form
{
// Обратите внимание на то, что модификатору доступа элемента
// управления txbUserName установлено значение public. Это сделано для того, чтобы
// можно было из другой формы получить введенный в него текст.
public System.Windows.Forms.TextBox txbUserName;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnOk;
private System.Windows.Forms.Label lblUserName;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public LogInForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.txbUserName = new System.Windows.Forms.TextBox();
this.btnCancel = new System.Windows.Forms.Button();
this.btnOk = new System.Windows.Forms.Button();
this.lblUserName = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// txbUserName
//
this.txbUserName.Location = new System.Drawing.Point(16, 32);
this.txbUserName.Name = "txbUserName";
this.txbUserName.Size = new System.Drawing.Size(312, 20);
this.txbUserName.TabIndex = 0;
this.txbUserName.Text = "";
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(152, 80);
this.btnCancel.Name = "btnCancel";
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Отмена";
//
// btnOk
//
this.btnOk.Location = new System.Drawing.Point(248, 80);
this.btnOk.Name = "btnOk";
this.btnOk.TabIndex = 2;
this.btnOk.Text = "ОК";
this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
//
// lblUserName
//
this.lblUserName.Location = new System.Drawing.Point(16, 8);
this.lblUserName.Name = "lblUserName";
this.lblUserName.Size = new System.Drawing.Size(216, 24);
this.lblUserName.TabIndex = 3;
this.lblUserName.Text = "Введите имя пользователя:";
//
// LogInForm
//
this.AcceptButton = this.btnOk;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(342, 116);
this.Controls.Add(this.lblUserName);
this.Controls.Add(this.btnOk);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.txbUserName);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "LogInForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Авторизация";
this.ResumeLayout(false);
}
#endregion
private void btnOk_Click(object sender, System.EventArgs e)
{
// Устанавливаем положительный результат выполнения
// и закрываем форму.
this.DialogResult = DialogResult.OK;
this.Close();
}
}
}
Последняя форма, которая будет использована в нашем проекте, — EditUserForm, изменяющая роль пользователей (рис. 10.17).
(рис 10.17) Форма EditUserFormЛистинг этой формы:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace CustomSecurity
{
/// <summary>
/// Форма изменения роли пользователя
/// </summary>
public class EditUserForm : System.Windows.Forms.Form
{
private System.Windows.Forms.Label lblName;
private System.Windows.Forms.Label lblRole;
public System.Windows.Forms.ComboBox cmbRole;
private System.Windows.Forms.Label lblUserName;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnEdit;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private string UserName;
// Обратите внимание на то, что у формы переопределен конструктор.
// Это сделано для передачи форме имени пользователя,
// роль которого необходимо изменить.
public EditUserForm(string userName)
{
UserName = userName;
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support — do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(EditUserForm));
this.lblName = new System.Windows.Forms.Label();
this.lblRole = new System.Windows.Forms.Label();
this.btnCancel = new System.Windows.Forms.Button();
this.btnEdit = new System.Windows.Forms.Button();
this.cmbRole = new System.Windows.Forms.ComboBox();
this.lblUserName = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// lblName
//
this.lblName.Location = new System.Drawing.Point(16, 16);
this.lblName.Name = "lblName";
this.lblName.Size = new System.Drawing.Size(144, 23);
this.lblName.TabIndex = 1;
this.lblName.Text = "Имя пользователя";
//
// lblRole
//
this.lblRole.Location = new System.Drawing.Point(16, 48);
this.lblRole.Name = "lblRole";
this.lblRole.Size = new System.Drawing.Size(144, 23);
this.lblRole.TabIndex = 3;
this.lblRole.Text = "Роль пользователя";
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(192, 112);
this.btnCancel.Name = "btnCancel";
this.btnCancel.TabIndex = 4;
this.btnCancel.Text = "Отмена";
//
// btnEdit
//
this.btnEdit.Location = new System.Drawing.Point(72, 112);
this.btnEdit.Name = "btnEdit";
this.btnEdit.TabIndex = 5;
this.btnEdit.Text = "Изменить";
this.btnEdit.Click += new System.EventHandler(this.button2_Click);
//
// cmbRole
//
this.cmbRole.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbRole.Items.AddRange(new object[] {
"admin",
"manager",
"operator"});
this.cmbRole.Location = new System.Drawing.Point(176, 48);
this.cmbRole.Name = "cmbRole";
this.cmbRole.Size = new System.Drawing.Size(200, 21);
this.cmbRole.TabIndex = 6;
//
// lblUserName
//
this.lblUserName.Location = new System.Drawing.Point(176, 16);
this.lblUserName.Name = "lblUserName";
this.lblUserName.Size = new System.Drawing.Size(200, 23);
this.lblUserName.TabIndex = 7;
//
// EditUserForm
//
this.AcceptButton = this.btnEdit;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(386, 152);
this.Controls.Add(this.lblUserName);
this.Controls.Add(this.cmbRole);
this.Controls.Add(this.btnEdit);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.lblRole);
this.Controls.Add(this.lblName);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "EditUserForm";
this.Text = "Изменение пользователя";
this.Load += new System.EventHandler(this.EditUserForm_Load);
this.ResumeLayout(false);
}
#endregion
private void button2_Click(object sender, System.EventArgs e)
{
// Устанавливаем положительный результат выполнения
// и закрываем форму.
this.DialogResult = DialogResult.OK;
this.Close();
}
private void EditUserForm_Load(object sender, System.EventArgs e)
{
// При загрузке формы устанавливаем имя пользователя в
//элемент управления lblUserName
lblUserName.Text = UserName;
}
}
}
Запустите приложение. При загрузке проверяется имя пользователя и инициализируется его роль. В зависимости от роли в приложении интерфейс изменяется следующим образом:
На диске, прилагаемом к книге, вы найдете приложение CustomSecurity (Code\Glava10\ CustomSecurity).
Операционная система Windows, включая самые последние на сегодняшний день версии Windows XP/2003, содержит ряд уязвимостей, немедленно замечаемых заинтересованными лицами. В Интернете встречается программка PassView (http://www.nht-team.org/) — она позволяет узнавать пароли от многих программ, установленных на вашем компьютере, среди которых Internet Explorer, Outlook Express, ICQ. Программа в заархивированном виде занимает около 60 Кб и не требует установки, кроме всего прочего, она позволяет увидеть пароли от диалапа – для этого достаточно получить доступ с правами администратора системы на считанные секунды и запустить PassView (рис. 10.1).
(рис 10.1) Вкладка Ras программы PassView, на которую выводятся данные всех соединений данного пользователяПолучение пароля администратора в Windows 2000/XP/2003 тоже не слишком сложная задача. Рассмотрим один из способов — восстановление пароля из SAM-базы. Все логины и пароли учетных записей хранятся в зашифрованном виде в файле SAM, расположенном в каталоге C:\WINDOWS\System32\Config. При попытке открыть этот файл непосредственно, из операционной системы появляется сообщение системы о нарушении доступа к файлу. Дело в том, что все файлы, находящиеся в этой папке, представляют собой фрагменты реестра, постоянно используемые системой, поэтому доступ к ним возможен не из самой ОС. Для этого применяем любой файловый менеджер (обычно на мультизагрузочных компакт-дисках можно найти их, причем как для файловой системы FAT32, так и для NTFS) для просмотра содержимого жесткого диска в режиме DOS и копируем на любой съемный носитель два файла — SAM и SYSTEM, содержащей системный ключ Syskey. Теперь на другом компьютере нам осталось только расшифровать содержимое скопированных файлов. Запускаем программу SamInside (в заархивированном виде около 800 Кб, http://www.insidepro.com/) и импортируем сначала файл SAM, а затем SYSTEM. Программа SamInside поддерживает перебор паролей по двум алгоритмам — LM и NT, кроме того, предоставляет возможность для определения диапазона предполагаемого пароля, включая перебор по словарям (рис. 10.2).
(рис 10.2) Настройки подбора пароляВ качестве пароля на свою учетную запись я установил password12345, и на компьютере P IV 2.4 ГГц, 512 Мб RAM получил часть пароля по алгоритму LM всего за 15 минут. Даже подбор по алгоритму NT, с учетом параметров, указанных на рис. 10.2, занимает всего 2 часа (рис. 10.3).
(рис 10.3) Окно программы в режиме определения подбора пароля по алгоритму NTКонечно, подбор сложного пароля, содержащего в случайном порядке буквы, числа и символы, займет гораздо больше времени, но привлечение больших вычислительных мощностей позволит справиться и с этой задачей. Мы не будем рассматривать здесь применение криптографических алгоритмов, теорию цифровых подписей или социальную инженерию — для этого бы потребовалось отдельная книга. Пример с учетными записями Windows должен просто навести вас на мысль, что любой инструмент безопасности требует критического и внимательного отношения к себе. Не следует всецело полагаться на ответственность разработчиков .NET Framework — всякий раз, когда в своих приложениях вы будете использовать один из принципов, речь о которых пойдет ниже, старайтесь оценивать все возможные контрмеры, способствующие исключению несанкционированного доступа.
Модель безопасности .NET Framework основывается на безопасности доступа кода (Code Access Security,
Под безопасностью доступа кода подразумевается разрешение на использование защищенных данных вашим приложением только в том случае, когда у него есть соответствующие права доступа. В тот момент, когда ваше приложение отправляет запрос на использование того или иного ресурса, .NET Framework проверяет наличие соответствующих прав доступа, и если таковых не обнаружено, запрещает доступ.
Под ролевой безопасностью подразумевается использование встроенных ролей. В зависимости от роли работающего пользователя приложение использует те или иные ресурсы.
Основой определения разрешений для системы безопасности служит само приложение, его сборка и так называемое удостоверение. Удостоверение — это набор информации о сборке и ее производителе. Удостоверение может включать в себя:
Производитель сборки может также включить и другую информацию о сборке, которая будет учитываться в том случае, если политика безопасности компьютера настроена на использование частной информации.
Данные, описывающие информацию о сборке, различаются по надежности. Например, строгое имя или производитель имеют больший приоритет, потому что их тяжелее подделать, в сравнении с зоной сборки и адресом сайта. Предоставлять доступ на основе недостаточно надежных данных о сборке представляется опасным, но если совмещать все данные о сборке, то можно добиться неплохого результата.
Разрешения — это право на использование частных ресурсов пользователя. Например, среди действий, защищаемых разрешениями доступа, есть такие, как чтение и запись файла, доступ к данным операционной системы или доступ к данным, находящимся в локальной базе данных.
.NET Framework имеет много встроенных разрешений, автоматически проверяющих код и разрешения на него. В таблице 10.1 перечислены все встроенные разрешения.
| Класс, представляющий разрешение | Охраняемый ресурс |
|---|---|
DirectoryServicesPermission |
Служба каталогов |
DnsPermission |
Службы DNS (Domain Namespace Services) |
EnvironmentPermission |
Ресурсы операционной системы |
EventLogPermission |
Журнал событий |
FileDialogPermission |
Диалоги открытия, сохранения файлов в пользовательском интерфейсе приложения |
FileIOPermission |
Работа с файлами |
IsolatedStorgeFilePermission |
Изолированное хранилище файлов |
MessageQueuePermission |
Очереди сообщений |
OleDbPermission |
Базы данных, доступ к которым осуществляется средствами поставщика данных OleDB |
PerformanceCounterPermission |
Счетчики производительности |
PrintingPermission |
Печать |
ReflectionPermission |
Информация о типах во время выполнения |
RegistryPermission |
Реестр |
SecurityPermission |
Работа с правами доступа |
ServiceControllerPermission |
Запуск или остановка Windows-служб |
SocketPermission |
Соединение с компьютером через порты |
SqlClientPermission |
Базы данных, доступ к которым осуществляется средствами SqlClient провайдера |
UIPermission |
Пользовательский интерфейс |
WebPermission |
Соединение с компьютерами через web-интерфейс |
В дополнение к встроенным разрешениям вы можете создать собственные, основываясь на классе CustomPermission.
.NET Framework принудительно налагает разрешения на управляемый код. Каждая загружаемая сборка получает набор разрешений на доступ к различным ресурсам. Эти разрешения основаны на политике безопасности. В свою очередь, политика безопасности использует удостоверение сборки для определения набора разрешений. Другими словами, .NET Framework применяет политику безопасности компьютера для назначения набора разрешений.
Группа кода состоит из
Сборка получает общий набор разрешений от всех членств, к которым она принадлежит.
Существует несколько уровней безопасности: сеть, компьютер, пользователь,
| Уровень безопасности | Описание |
|---|---|
| Сеть ( |
Уровень контролируется администратором сети и содержит группы кода, которые могут быть назначены любому управляемому коду во всей корпоративной сети |
| Компьютер (Machine) | Уровень контролируется администратором компьютера и содержит группы кода, которые могут быть назначены любому управляемому коду, выполняемому на данном компьютере |
| Пользователь (User) | Уровень контролируется пользователем компьютера и содержит группы кода, которые могут быть назначены любому управляемому коду, выполняемому от имени данного пользователя |
| Дополнительный уровень, позволяющий изолировать, выгружать и ограничивать выполняемый управляемый код |
Окончательный набор разрешений получается в результате суммы всех разрешений от каждой сборки, т.к. каждая сборка может иметь свой набор разрешений (рис. 10.4).
(рис 10.4) Принцип работы безопасности доступа кодаСреда выполнения Common Language Runtime (CLR) также имеет стандартную политику безопасности и использует ее набор разрешений ( табл. 10.3).
| Отсутствие разрешений (Nothing) | Отсутствуют какие-либо разрешения. Код не может быть запущен. Код из "небезопасной" зоны использует эту политику безопасности. |
| Извлечение (Execution) | Предоставляет разрешение только на запуск приложения. Не разрешает приложению использовать какие-либо ресурсы |
| Интернет (Internet) | Предоставляет разрешения на выполнение кода, открытие диалоговых окон, создание web-соединений с сайтом, с которого установлено приложение, и использование изолированного хранилища с ограниченным размером. По умолчанию, весь код из Интернета и "небезопасные" зоны используют эту политику безопасности |
| Корпоративная интрасеть (Local Intranet) | Предоставляет разрешения на запуск кода; его выполнение без ограничений; использование изолированного хранилища без лимита; использование DNS-служб; использование таких данных, как имя пользователя и временных переменных; создание web-соединения с сайтами, с которых взяты сборки, используемые в приложении; чтение и сохранение файлов из того же каталога, что и приложение. По умолчанию, весь код из сети использует эту политику безопасности |
| Весь доступ (Everything) | Предоставляет все стандартные разрешения, кроме отмены верификации |
| Полный доступ (Full Trust) | Предоставляет полный доступ ко всем ресурсам. По умолчанию, весь управляемый код локального компьютера использует эту политику безопасности |
Каждое приложение, которое использует CLR, должно взаимодействовать с системой безопасности во время выполнения. Когда приложение выполняется, CLR автоматически проверяет все сборки приложения и возвращает набор разрешений. В зависимости от набора разрешений или ресурсов, с которыми работает приложение, оно либо выполняет заданные действия, либо генерирует исключение безопасности.
Настройки безопасности каждого компьютера индивидуальны. Они меняются в зависимости от пользователя, администратора компьютера или администратора сети, и нет никакой гарантии, что ваше приложение получит необходимые разрешения на выполнение операций.
Взаимодействие с системой безопасности во время выполнения осуществляется двумя способами: императивным и декларативным. Декларативный способ взаимодействия осуществляется с помощью атрибутов, императивный – с помощью объектов классов в вашем приложении. Некоторые операции могут быть выполнены с помощью одного из способов, а некоторые — любым из них.
Из-за того, что большинство разрешений уже встроены в .NET Framework, практически не приходится создавать собственные разрешения. Выполняемый код, взаимодействующий с определенными ресурсами, проходит проверку и получает разрешения на использование данных ресурсов. Итак, основной задачей на стадии разработки остается лишь документирование необходимых приложению разрешений для того, чтобы быть уверенным, что приложение их получит. Это делается с помощью запроса разрешений и утилит настройки политики безопасности.
Для того чтобы добавить запрос на определенное разрешение, необходимо добавить соответствующий атрибут в файл ApplicationInfo.cs проекта. При компиляции сборки эти атрибуты сохранятся в метаданных. Запросы на разрешения проверяются CLR во время загрузки сборки. Существует три вида запросов, описание которых приводится в таблице 10.4.
| Запрос | Описание запроса |
|---|---|
| Запрос минимального набора разрешений (RequestMinimum) | Запрашивает необходимый минимум для корректной работы приложения. Если это разрешение не назначено политикой безопасности, то CLR не выполнит операции, а сгенерирует исключение типа PolicyException. Этим запросом необходимо пользоваться только для минимальных разрешений |
| Запрос дополнительного набора разрешений (RequestOptional) | Представляет набор разрешений, с которыми может работать ваше приложение, но без которых оно сможет корректно выполнять действия. Этим запросом описывают дополнительную функциональность приложения. Если это разрешение не назначено политикой безопасности, то приложение будет продолжать работать |
| Запрос на отказ от разрешений (RequestRefused) | Указываются разрешения, которые ваше приложение использовать не будет. Запросы указываются для обеспечения дополнительной безопасности |
В лекции 2 мы работали с проектом BinaryReadWrite. Скопируйте всю папку проекта и переименуйте ее в Permissions. Снова поместите в папку bin\Debug какой-нибудь файл формата MP3 и назовите его music.mp3. Запустите проект и убедитесь в том, что в этой же папке появился записанный файл newmusic.mp3. Теперь добавляем пространство имен System.Security и соответствующий блок исключений:
using System;
using System.IO;
using System.Security;
namespace BinaryReadWrite
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
//Добавляем блок обработки исключений
try
{
//Создаем новый поток Chtenie и указываем файл для чтения — music.mp3
FileStream Chtenie = new FileStream("music.mp3", FileMode.Open);
//Создаем экземпляр br класса BinaryReader и связываем его с потоком Chtenie
BinaryReader br = new BinaryReader(Chtenie);
// Создаем массив типа байтов и
//передаем ему размер в байтах — (например, 1000000 ) записываемого файла
byte[] n = br.ReadBytes(1000000);
//Закрываем поток
br.Close();
//Создаем новый поток Zapis и указываем название файла для записи sozdanajamuzika.mp3
FileStream Zapis = new FileStream("newmusic.mp3", FileMode.CreateNew, FileAccess.Write);
//Создаем экземпляр wr класса BinaryWriter и связываем его с потоком Zapis
BinaryWriter wr = new BinaryWriter(Zapis);
// Можно связывать объект wr с потоком, создавая его без названия:
//BinaryWriter wr = new BinaryWriter(new FileStream("sozdanajamuzika.mp3", FileMode.CreateNew, FileAccess.Write));
//Передаем в поток массив байтов n
wr.Write(n);
//Закрываем поток
br.Close();
}
catch (SecurityException ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
В окне Solution Explorer дважды щелкаем на файл AssemblyInfo.cs и для использования атрибутов добавляем пространство имен System.Security.Permissions:
using System.Reflection; using System.Runtime.CompilerServices; using System.Security.Permissions;
В конце листинга AssemblyInfo.cs определяем минимальный набор разрешений:
// Запрос минимального набора разрешений [assembly: UIPermission(SecurityAction.RequestOptional, Window = UIPermissionWindow.SafeTopLevelWindows)] [assembly: PermissionSet(SecurityAction.RequestOptional, Name = "LocalIntranet")]
Это разрешение не было назначено политикой безопасности, поэтому генерируется исключение (рис. 10.5).
(рис 10.5) Сгенерированное исключение при определении минимального набора разрешенийДля получения доступа следует определять другие наборы разрешений и настраивать политику безопасности. Например, данный запрос разрешений указывает, что сборка не нуждается в дополнительных разрешениях:
[assembly:PermissionSet(SecurityAction.RequestOptional, Unrestricted = false)]
На диске, прилагаемом к книге, вы найдете приложение Permissions (Code\Glava10\ Permissions).
С помощью утилиты Microsoft .NET Framework 1.1 Configuration можно настраивать политику безопасности на компьютере. Для ее запуска переходим в Мой компьютер\Панель управления\Администрирование\Microsoft .NET Framework 1.1 Configuration. Открываем вкладку Runtime Security Policy (рис. 10.6).
(рис 10.6) Утилита Microsoft .NET Framework 1.1 ConfigurationОкно программы разделено на две части. В левой мы можем выбирать нужный объект, в правой немедленно выводится панель информации с описанием этого объекта и ссылками на возможные действия. Для каждого уровня безопасности — Enterprise, Machine и User (описание см. в табл. 10.2) — имеется своя настройка трех категорий — Code Groups, Permission Sets и Policy Assemblies. Поскольку сами категории совершенно одинаковы для всех уровней, выберем один уровень — Machine — и рассмотрим настройку доступа. Переходим на вкладку Machine —> Code Groups —> All Code —> My_Computer_Zone. На панели описания мы видим описание данной группы кода и ее свойства — условия членства в группе и набор разрешений для данной группы (рис. 10.7).
(рис 10.7) Свойства группы My_Computer_ZoneВ этой группе кода определяется доступ на локальном компьютере, аналогично на вкладках LocalIntranet_Zone и Internet_Zone — доступ внутри локальной сети и в Интернете. Если настройка доступа для групп кода My_Computer_Zone и LocalIntranet_Zone представляет ограниченный интерес, то изменение свойств Internet_Zone требуется гораздо чаще — ведь большинство деструктивных действий, совершаемых без ведома пользователя, осуществляется через Интернет. Щелкнем правой кнопкой мыши на группе Internet_Zone и в появившемся контекстном меню выберем "Свойства". На вкладке Membership Condition по умолчанию определено
(рис 10.8) Условие вступления Strong Name в членство группы Internet_ZoneПосле введения необходимых данных переключаемся на вкладку Permission Set, где определяем набор разрешений для этой группы. После завершения редактирования и закрытия окна свойств изменения вступят в силу. Для создания собственного набора разрешений щелкаем правой кнопкой на вкладке Permission Sets и выбираем пункт меню New (рис. 10.9).
(рис 10.9) Создание набора разрешений Permission SetsВ появившемся окне вводим имя нового набора разрешений и его описание (рис. 10.10).
(рис 10.10) Создание нового набора разрешенийВ следующем окне выбираем набор из предлагаемого списка разрешений (рис. 10.11).
(рис 10.11) Добавление разрешений к новому наборуПри выборе каждого разрешения появляется окно дополнительных свойств, предлагающих более детальную настройку — например, доступ только для чтения, для чтения и записи или только записи. Созданный набор разрешений отображается в списке Permission Sets и может быть использован для настройки политики безопасности учетной записи, компьютера или корпоративной сети.
Утилита командной строки caspol.exe, поставляемая с .NET Framework, позволяет просматривать и манипулировать политикой безопасности. Файл утилиты располагается в каталоге С\WINDOWS\Microsoft.NET\Framework\ v1.1.4322\ CasPol.exe.
Утилита caspol.exe отображает всю доступную информацию об уровне безопасности, установленном по умолчанию (Machine). Эта информация включает в себя иерархию групп кода, название набора разрешений и имена сборок, использующих политику Full Trust.
Утилита caspol.exe имеет следующий синтаксис:
caspol <параметры> <аргументы>
В таблице 10.5 приводится список некоторых параметров утилиты.
| Параметр | Действие | Примечания |
|---|---|---|
-l |
Список (List) | Вывод всей доступной информации об уровне безопасности, установленном по умолчанию |
-lg |
Список групп кода (List Group Code) | Вывод списка групп кода уровня безопасности, установленного по умолчанию |
-lp |
Список наборов разрешений (List Permission Set) | Вывод списка наборов разрешений |
-ag |
Добавление группы кода (Add Code Group) | Добавление новой группы кода в уровень безопасности |
-url |
URL | Использование сборкой URL в качестве условия принадлежности к группе |
| -n | Название группы кода (Code Group Name) | Задание имени для новой группы кода |
-exclusive |
Установка флага Exclusive для уровня безопасности | Используется для тестирования. Любая сборка, встретившая условие группы кода, получает соответствующий набор разрешений. Например, это может применяться для предотвращения автоматического назначения набора разрешений Full Trust |
-cg |
Поменять группу кода (Change Code Group) | Изменение существующей группы кода |
-rg |
Удалить группу кода (Remove Code Group) | Удаление существующей группы кода |
Для работы с утилитой выбираем Пуск\Все программы\ Microsoft Visual Studio .NET 2003 \ Visual Studio .NET Tools \ Visual Studio .NET 2003 Command Prompt и в запустившейся командной строке вводим
caspol.exe
Появляется справка утилиты, которую затем можно снова вывести одной из команд
caspol –help caspol -? caspol /?
Для вывода информации о группах кода и наборов разрешений вводим
caspol -l
Для получения списка групп кода вводим
caspol -lg
Для просмотра списка наборов разрешений вводим
caspol –lp
Для сброса всех пользовательских настроек вводим
caspol –reset
По умолчанию весь код, запускающийся на локальной машине, получает набор разрешений полного доступа Full Trust. Нам может потребоваться, для тестовых целей, чтобы код выполнялся под другим набором разрешений. Самый простой способ сделать это — создать папку и настроить политику безопасности для причисления этой папки к другой зоне. Это делается следующим образом.
Создаем папку C:\testpermissions, затем в командной строке создаем новую группу разрешений:
сaspol –ag 1 –url file:///C:/testfolder/ * Internet -n test_permissions_group –exclusive on
Этот код добавляет новую группу кода test_permissions_group, условием вступления в которую является запуск из папки testpermissions. После этого копируем приложение в папку testpermissions и запускаем его с набором разрешений Internet.
Изменяем набор разрешений на Execution командой
Caspol –cg Test_Group Execution
Удаляем тестовую группу командой
Caspol –rg Test_Group
Ролевая модель безопасности использует данные пользователя в качестве источника информации. Как правило, эта информация состоит из имени и роли пользователя.
Ролевая модель безопасности в .NET Framework использует две концепции: личность (identity), представляющее собой имя пользователя, и его роль (
.NET Framework позволяет разработчикам применять встроенный в Microsoft Windows набор ролей. Также вы можете задействовать и частный набор ролей.
Перед тем как использовать роль пользователя, нам необходимо проверить личность этого пользователя. Аутентификация — это обнаружение и проверка личности пользователя путем сравнения введенной пользователем информации с существующими данными. Например, при проверке электронной почты вы вводите имя пользователя и пароль. Сервер сверяет их со своими данными и, если они идентичны, открывает доступ к вашей почте.
Сегодня существует множество механизмов аутентификации пользователей. Некоторые из них могут использоваться в .NET Framework приложениях. В операционной системе Windows используется механизм Microsoft
После того как мы удостоверились в личности пользователя, он проходит авторизацию. Авторизация — это определение прав пользователя на выполнение тех или иных действий, после чего ему предоставляется возможность работы с определенными ресурсами.
Объекты "личность" и "роль" применяются для доступа к информации о пользователе. Мы можем использовать информацию об имени встроенной учетной записи в операционной системе Windows и роли этой учетной записи с помощью объектов WindowsIdentity и WindowsPrincipal. Для доступа к информации о пользователе, основанной на собственном механизме аутентификации, задействуются объекты GenericIdentity и GenericPrincipal.
Объект "личность" инкапсулирует такую информацию о пользователе, как имя и тип аутентификации. В библиотеке .NET Framework есть три типа объекта "личность" (таблица 10.6).
| Личность | Описание |
|---|---|
| Windows Identity | Представляет информацию о текущей учетной записи Windows. Windows Identity позволяет определять полномочия пользователя в ОС и использовать их в приложении. Реализуется классом WindowsIdentity |
| Generic Identity | Представляет информацию о пользователе, которая основана на частном механизме аутентификации, используемом в приложении. Реализуется классом GenericIdentity |
| Custom Identity | Представляет "личность", которая инкапсулирует частную информацию о пользователе. Реализуется классом, в котором имеется интерфейс IIdentity |
Все классы, описывающие "личность", должны реализовывать интерфейс IIdentity. Интерфейс IIdentity имеет три открытых свойства:
Name — имя пользователя.IsAuthenticated — логическая переменная, принимающая одно из двух значений в зависимости от результата прохождения пользователем аутентификации.AuthenticationType — тип аутентификации пользователя."Роль" определяет право пользователей на выполнение действий, причем возможно наличие нескольких пользователей (входящих в группу) с различными именами, но равными ролями. Практически такая ситуация характерна для информационных систем — например, в банке права менеджера и оператора различаются, причем может быть несколько как менеджеров, так и операторов.
В библиотеке .NET Framework есть три типа объекта "роль" (таблица 10.7).
| Роль | Описание |
|---|---|
| Windows |
Представляет пользователей операционной системы Windows и их роли. Реализуется классом WindowsPrincipal |
| Generic |
Представляет независимых от операционной системы пользователей и их роли. Реализуется классом GenericPrincipal |
| Custom |
Представляет специальную для приложения информацию о ролях. Реализуется классом, в котором имеется интерфейс IPrincipal |
Все классы, описывающие объект "роль", должны реализовывать интерфейс IPrincipal. Интерфейс IPrincipal имеет одно свойство — Identity, которое описывает объект класса, реализующего интерфейс Iidentity, и метод — IsInRole, который определяет, принадлежит ли текущий "принципал" к заданной роли.
Существует два способа создания объекта WindowsPrincipal. Разница между ними в том, что один способ проверяет введенную информацию один раз, а второй — постоянно.
Если однократной проверки роли достаточно, то создаем объект WindowsPrincipal следующим образом:
// Инициализируем объект класса WindowsIdentity
// с помощью статического метода WindowsIdentity.GetCurrent()
WindowsIdentity identity = WindowsIdentity.GetCurrent();
// Создаем новый объект класса WindowsPrincipal
// и передаем в качестве параметра объект identity
WindowsPrincipal principal = new WindowsPrincipal(identity);
Если же вам необходимо проверять роль пользователя постоянно, то лучше создать объект WindowsPrincipal следующим образом:
// Указываем домену приложения политику ролевой безопасности // с помощью члена WindowsPrincipal перечисления PrincipalPolicy, // переданного в статический метод AppDomain.CurrentDomain. // SetPrincipalPolicy. AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); // После того как новая политика установлена, вы можете использовать // объект из статического свойства CurrentPrincipal класса Thread. WindowsPrincipal principal = System.Threading.Thread.CurrentPrincipal as WindowsPrincipal;
Создайте новое консольное приложение и назовите его WindowsIdentityandPrincipal. Далее привожу листинг с комментариями:
using System;
//Подключаем пространства имен Threading и Principal
using System.Threading;
using System.Security.Principal;
namespace WindowsIdentityandPrincipal
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
// Создаем и инициализируем объекты
// WindowsPrincipal и WindowsIdentity
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
// Выводим информацию, содержащуюся в объекте класса WindowsPrincipal
Output("Name", principal.Identity.Name);
Output("Type", principal.Identity.AuthenticationType);
Output("Authenticated", principal.Identity.IsAuthenticated.ToString());
// Выводим информацию, содержащуюся в объекте класса WindowsIdentity
Output("IdentName", identity.Name);
Output("IdentType", identity.AuthenticationType);
Output("IdentIsAuthenticated", identity.IsAuthenticated.ToString());
Output("IsAnonymous", identity.IsAnonymous.ToString());
Output("IsGuest", identity.IsGuest.ToString());
Output("IsSystem", identity.IsSystem.ToString());
Output("Token", identity.Token.ToString());
}
//Метод Output выводит информацию на экран, причем myName — это название
//параметра, а myValue — его значение.
public static void Output(string myName, string myValue)
{
Console.WriteLine(myName + "= {0}", myValue);
}
В результате выводится полный список значений WindowsIdentity и WindowsPrincipal на данном компьютере (рис. 10.12).
(рис 10.12) Результат приложения WindowsIdentityandPrincipalНа диске, прилагаемом к книге, вы найдете приложение WindowsIdentityandPrincipal (Code\Glava10\ WindowsIdentityandPrincipal).
Классы GenericPrincipal совместно с классом GenericIdentity применяются для реализации ролевой безопасности, не зависящей от системы безопасности Windows. Например, у пользователя запрашиваются имя и пароль, которые затем сверяются с данными, хранящимися в приложении или локальной базе данных, и, после успешной проверки, создаются объекты GenericPrincipal и GenericIdentity на основании введенных значений.
Для реализации ролевой безопасности на основе объектов классов GenericPrincipal и GenericIdentity необходимо выполнить следующие действия:
GenericIdentity и инициализировать его именем пользователя;GenericPrincipal и инициализировать его только что созданным объектом класса GenericIdentity и строковым массивом, содержащим роли пользователя;GenericPrincipal к текущему потоку. Прикрепление к текущему потоку необходимо для осуществления последующей проверки полномочий пользователя в коде приложения.Синтаксис этих действий следующий:
// Создаем и инициализируем именем пользователя
// объект класса GenericIdentity
GenericIdentity identity = new GenericIdentity("Username");
// Создаем строковый массив, содержащий роли пользователя.
string[] userRoles = new string[]{"Administrator", "PowerUser", "GuestUser"};
// Создаем объект класса GenericPrincipal и инициализируем его
// объектом класса GenericIdentity identity и строковым массивом с
// ролями.
GenericPrincipal principal = new GenericPrincipal(identity, userRoles);
// Прикрепляем объект роли к текущему процессу.
// Этот и все дочерние процессы будут иметь данную роль.
Thread.CurrentPrincipal = principal;
Создайте новое консольное приложение и назовите его GenericIdentityandPrincipal . Далее привожу листинг с комментариями:
using System;
using System.Threading;
using System.Security.Principal;
namespace GenericIdentityandPrincipal
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
[STAThread]
static void Main(string[] args)
{
// Создаем и прикрепляем объект роли.
CreateGenericPrincipalAndIdentity(Roles.PowerUser);
// Вводим один из способов осуществления проверки роли.
if(Thread.CurrentPrincipal.IsInRole("User"))
{
Console.WriteLine("У вас нет прав для работы с этим приложением");
}
else if(Thread.CurrentPrincipal.IsInRole("PowerUser"))
{
Console.WriteLine("Добро пожаловать, {0}. Ваша роль — Продвинутый пользователь", Thread.CurrentPrincipal.Identity.Name);
}
else // Administrator
{
Console.WriteLine("Добро пожаловать, {0}. Ваша роль — Администратор", Thread.CurrentPrincipal.Identity.Name);
}
}
// Создание объектов личности и роли
static void CreateGenericPrincipalAndIdentity(Roles role)
{
// Создаем и инициализируем именем пользователя
// объект класса GenericIdentity
GenericIdentity identity = new GenericIdentity("Username");
// Создаем строковый массив, содержащий роли пользователя.
string[] userRoles = new string[]{role.ToString()};
// Создаем объект класса GenericPrincipal и инициализируем его
// объектом класса GenericIdentity identity и строковым массивом с ролями.
GenericPrincipal principal = new GenericPrincipal(identity, userRoles);
// Прикрепляем объект роли к текущему процессу.
// Этот и все дочерние процессы будут иметь данную роль.
Thread.CurrentPrincipal = principal;
}
enum Roles
{
Administrator,
PowerUser,
User
}
}
}
Если пользователь проходит проверку, он получает дальнейший доступ (рис. 10.13).
(рис 10.13) Результат приложения GenericIdentityandPrincipalНа диске, прилагаемом к книге, вы найдете приложение GenericIdentityandPrincipal (Code\Glava10\ GenericIdentityandPrincipal).
После создания объекта роли к нему можно добавить код для реализации контроля доступа, основанного на ролях пользователей. Рассмотрим пример, в котором осуществляется контроль доступа по имени пользователя, причем сравнение строк не чувствительно к регистру. Создайте новое консольное приложение и назовите его AccessControl. Далее привожу листинг с комментариями:
using System;
using System.Threading;
using System.Security.Principal;
namespace AccessControl
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
GenericIdentity identity = new GenericIdentity("MANAGER");
string[] userRoles = new string[]{"Administrator"};
GenericPrincipal principal = new GenericPrincipal(identity, userRoles);
Thread.CurrentPrincipal = principal;
ValidateUserName();
ValidateRole();
}
//Проверяем пользователя по личности
static void ValidateUserName()
{
if (String.Compare(Thread.CurrentPrincipal.Identity.Name, "manager", true) == 0)
{
Console.WriteLine("Добро пожаловать, пользователь Manager");
}
else
{
throw new System.Security.SecurityException("У вас нет прав для выполнения текущей операции");
}
}
//Проверяем пользователя по роли
static void ValidateRole()
{
if (Thread.CurrentPrincipal.IsInRole("Administrator"))
{
Console.WriteLine("Добро пожаловать, пользователь Manager");
}
else
{
throw new System.Security.SecurityException("У вас нет прав для выполнения текущей операции");
}
}
}
}
При запуске этого приложения дважды осуществляется авторизация – по имени пользователя (личности) и его роли (рис. 10.14):
(рис 10.14) Результат работы приложения AccessControlДля объектов класса WindowsIdentity имя пользователя представлено в виде имени пользователя и домена. Например, если бы приведенный выше пример использовал объект класса WindowsIdentity, то имя для сравнения было бы следующим: DOMAIN\Manager.
Для объектов класса WindowsPrincipal роль включает в себя и имя домена. Ниже представлен пример проверки роль объектом класса WindowsPrincipal:
if(WindowsPrincipalObj.IsInRole("DOMAIN\\ Manager "))
// Разрешить действие
Если учесть, что ваше приложение может кочевать по сети в поисках нового пользователя, то строго прописывать имя домена не рекомендуется. Вместо этого для проверки ролей пользователей объектом класса WindowsPrincipal нужно указывать не строку с названием роли, а член перечисления WindowsBuiltInRole, как показано ниже:
MyPrincipal.IsInRole(WindowsBuiltInRole. Manager);
На диске, прилагаемом к книге, вы найдете приложение AccessControl (Code\Glava10\ AccessControl).
Мы обсудили теоретические основы безопасности .NET Framework, займемся теперь практическим использованием этих концепций. Создайте новое Windows-приложение и назовите его CustomSecurity. При запуске приложение будет требовать авторизацию пользователя и, в зависимости от его роли, предоставлять разные права доступа. В главной форме будет отображаться список всех пользователей и предоставляться возможность изменять роль пользователей. Список пользователей будет содержаться в файле Users.xml, для его создания в окне щелкаем правой кнопкой мыши в окне Solution Explorer и выбираем Add/Add New Item… . В появившемся окне выбираем XML File. Создадим нескольких пользователей, относящихся к трем группам — admin, manager, operator:
<?xml version="1.0" encoding="utf-8" ?>
<users>
<user name="admin" id="1" role="admin" />
<user name="manager" id="2" role="manager" />
<user name="accountant" id="3" role="accountant" />
<user name="student1" id="4" role="operator" />
<user name="student2" id="5" role="operator" />
<user name="student3" id="6" role="operator" />
</users>
Для просмотра и редактирования данных в виде таблицы щелкните на кнопке
, расположенной внизу на панели. Закончив редактирование, скопируйте файл Users.xml из папки проекта в папку bin/Debug.
Вынесем логику определения личности и роли пользователей в отдельные классы — CustomIdentity.cs и CustomPrincipal.cs. Для добавления в проект отдельных классов щелкаем в окне Solution Explorer правой кнопкой и выбираем Add/ Add Class… . Далее привожу листинг CustomIdentity.cs c комментариями:
using System;
using System.Security.Principal;
using System.Xml;
namespace CustomSecurity
{
/// <summary>
/// Класс CustomIdentity , описывающий "личность", наследует от класса IIdentity
/// </summary>
public class CustomIdentity : IIdentity
{
//Вводим переменные аутентификации
private bool _isAuth;
private string _name;
private string _authType;
private int _id;
/// <summary>
/// Конструктор.
/// </summary>
public CustomIdentity()
{
this._isAuth = false;
this._authType = String.Empty;
this._name = String.Empty;
this._id = -1;
}
/// <summary>
///Создаем конструктор, принимающий имя пользователя.
/// </summary>
/// <param name="userName">Имя пользователя.</param>
public CustomIdentity(string userName)
{
this._id = this.AuthUserName(userName);
this._name = userName;
this._isAuth = true;
this._authType = "Частный тип аутентификации.";
}
/// <summary>
/// Определяем уникальный идентификатор пользователя.
/// </summary>
public int ID
{
get { return this._id; }
}
#region IIdentity Members
/// <summary>
/// Проверка аутентификации пользователя.
/// </summary>
public bool IsAuthenticated
{
get
{
// Реализуем свойство интерфейса.
return this._isAuth;
}
}
/// <summary>
/// Определяем имя пользователя.
/// </summary>
public string Name
{
get
{
// Реализуем свойство интерфейса.
return this._name;
}
}
/// <summary>
/// Определяем тип аутентификации.
/// </summary>
public string AuthenticationType
{
get
{
// Реализуем свойство интерфейса.
return this._authType;
}
}
#endregion
/// <summary>
/// Проверяем, существует ли имя пользователя в базе данных — файле XML.
/// </summary>
/// <param name="name">Имя пользователя.</param>
/// <returns>ID пользователя.</returns>
private int AuthUserName(string name)
{
// Считываем и сравниваем имя пользователя.
XmlTextReader xmlReader = new XmlTextReader("Users.xml");
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
while(xmlReader.Read())
{
if(xmlReader["name"] == name)
return Int32.Parse(xmlReader["id"]);
}
// Если пользователь не найден, генерируем исключение.
throw new System.Security.SecurityException(String.Format("Пользователь {0} не найден в базе данных.", name));
}
}
}
Листинг CustomPrincipal.cs:
using System;
using System.Security.Principal;
using System.Xml;
namespace CustomSecurity
{
/// <summary>
/// Класс CustomPrincipal, описывающий роль, наследует от класса IPrincipal
/// </summary>
public class CustomPrincipal :IPrincipal
{
private CustomIdentity _indentity;
private string _role;
/// <summary>
/// Конструктор.
/// </summary>
/// <param name="identity">Определяем личность пользователя.</param>
public CustomPrincipal(CustomIdentity identity)
{
// Инициализируем личность
this._indentity = identity;
// Инициализируем переменную только один раз. Если роль изменится в процессе выполнения приложения, то
// изменения вступят в силу только после перезагрузки приложения.
this._role = this.GetUserRole();
}
#region IPrincipal Members
/// <summary>
/// Свойство личности пользователя.
/// </summary>
public IIdentity Identity
{
get
{
// Реализуем свойство интерфейса.
return this._indentity;
}
}
/// <summary>
/// Проверяем, прнадлежит ли пользователь к заданной роли.
/// </summary>
/// <param name="role">Роль.</param>
/// <returns></returns>
public bool IsInRole(string role)
{
// Реализуем метод интерфейса.
return role == this._role;
// Если необходимо реагировать на изменение роли без перезагрузки приложения, то это можно сделать так:
//return role == this.GetUserRole();
}
#endregion
/// <summary>
/// Возвращаем роль пользователя.
/// </summary>
/// <returns></returns>
private string GetUserRole()
{
// Считываем и сравниваем имя пользователя.
XmlTextReader xmlReader = new XmlTextReader("Users.xml");
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
while(xmlReader.Read())
{
if(xmlReader["name"] == this._indentity.Name)
return xmlReader["role"];
}
// Если роль пользователя не найдена, генерируем исключение.
throw new System.Security.SecurityException(String.Format("Роль пользователя {0} не найдена в базе данных.",
this._indentity.Name));
}
}
}
Теперь займемся пользовательским интерфейсом программы и привязкой обработчиков. Все приложения, которые мы рассматривали в предыдущих лекциях, я сопровождал описанием дизайна форм и не указывал в листинге область Windows Form Designer generated code. Сейчас я поступлю наоборот — я приведу код, генерируемый средой, и изображения формы, а вы, скопировав код, относящийся к дизайну, разберетесь с элементами, которые были использованы для создания интерфейса. Итак, главная форма программы будет иметь следующий вид (рис. 10.14):
(рис 10.15) Главная форма проекта CustomSecurityВ окне Solution Explorer переименуйте Form1.cs в MainForm.cs, добавьте на форму элемент управления ImageList и поместите в коллекцию две иконки (на рис. 10.15 эти иконки были использованы для кнопок панели инструментов).
Листинг формы MainForm:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Xml;
using System.Xml.XPath;
using System.IO;
namespace CustomSecurity
{
/// <summary>
/// Главная форма.
/// </summary>
public class MainForm : System.Windows.Forms.Form
{
private System.Windows.Forms.ToolBarButton btnViewUsers;
private System.Windows.Forms.ToolBarButton btnEditUsers;
private System.Windows.Forms.ImageList toolBoxIcons;
private System.Windows.Forms.ToolBar toolBar;
private System.Windows.Forms.ListView lstViewUsers;
private System.Windows.Forms.ColumnHeader colUserName;
private System.Windows.Forms.ColumnHeader colUserRole;
private System.Windows.Forms.ColumnHeader colUserId;
private System.ComponentModel.IContainer components;
public MainForm()
{
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support — do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(MainForm));
this.toolBar = new System.Windows.Forms.ToolBar();
this.btnViewUsers = new System.Windows.Forms.ToolBarButton();
this.btnEditUsers = new System.Windows.Forms.ToolBarButton();
this.toolBoxIcons = new System.Windows.Forms.ImageList(this.components);
this.lstViewUsers = new System.Windows.Forms.ListView();
this.colUserName = new System.Windows.Forms.ColumnHeader();
this.colUserRole = new System.Windows.Forms.ColumnHeader();
this.colUserId = new System.Windows.Forms.ColumnHeader();
this.SuspendLayout();
//
// toolBar
//
this.toolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {
this.btnViewUsers,
this.btnEditUsers});
this.toolBar.DropDownArrows = true;
this.toolBar.ImageList = this.toolBoxIcons;
this.toolBar.Location = new System.Drawing.Point(0, 0);
this.toolBar.Name = "toolBar";
this.toolBar.ShowToolTips = true;
this.toolBar.Size = new System.Drawing.Size(424, 28);
this.toolBar.TabIndex = 0;
this.toolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.toolBar_ButtonClick);
//
// btnViewUsers
//
this.btnViewUsers.ImageIndex = 0;
this.btnViewUsers.Tag = "view";
this.btnViewUsers.ToolTipText = "Вывести список пользователей";
//
// btnEditUsers
//
this.btnEditUsers.ImageIndex = 1;
this.btnEditUsers.Tag = "edit";
this.btnEditUsers.ToolTipText = "Редактировать роль пользователя";
//
// toolBoxIcons
//
this.toolBoxIcons.ImageSize = new System.Drawing.Size(16, 16);
this.toolBoxIcons.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("toolBoxIcons.ImageStream")));
this.toolBoxIcons.TransparentColor = System.Drawing.Color.Transparent;
//
// lstViewUsers
//
this.lstViewUsers.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.colUserName,
this.colUserRole,
this.colUserId});
this.lstViewUsers.Cursor = System.Windows.Forms.Cursors.Hand;
this.lstViewUsers.Dock = System.Windows.Forms.DockStyle.Fill;
this.lstViewUsers.FullRowSelect = true;
this.lstViewUsers.GridLines = true;
this.lstViewUsers.Location = new System.Drawing.Point(0, 28);
this.lstViewUsers.MultiSelect = false;
this.lstViewUsers.Name = "lstViewUsers";
this.lstViewUsers.Size = new System.Drawing.Size(424, 238);
this.lstViewUsers.SmallImageList = this.toolBoxIcons;
this.lstViewUsers.TabIndex = 1;
this.lstViewUsers.View = System.Windows.Forms.View.Details;
//
// colUserName
//
this.colUserName.Text = "Имя пользователя";
this.colUserName.Width = 125;
//
// colUserRole
//
this.colUserRole.Text = "Роль пользователя";
this.colUserRole.Width = 125;
//
// colUserId
//
this.colUserId.Text = "Уникальный идентификатор";
this.colUserId.Width = 175;
//
// MainForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(424, 266);
this.Controls.Add(this.lstViewUsers);
this.Controls.Add(this.toolBar);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "MainForm";
this.Text = "Ролевая безопасность";
this.Load += new System.EventHandler(this.MainForm_Load);
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}
private void MainForm_Load(object sender, System.EventArgs e)
{
// Получаем имя пользователя.
LogInForm login = new LogInForm();
if(login.ShowDialog() != DialogResult.OK)
{
// Если пользователь отменил ввод имени, закрываем приложение
this.Close();
return;
}
try
{
// Создаем объекты IIdentity и IPrincipal
CustomIdentity identity = new CustomIdentity(login.txbUserName.Text);
CustomPrincipal principal = new CustomPrincipal(identity);
Thread.CurrentPrincipal = principal;
this.ValidateUser();
}
catch(Exception ex)
{
// Обрабатываем исключения и закрываем приложение.
MessageBox.Show("Во время выполнения приложения возникла ошибка: "+ex.Message, "Ошибка");
this.Close();
return;
}
}
/// <summary>
/// Изменяем пользовательский интерфейс в зависимости от роли пользователя.
/// </summary>
private void ValidateUser()
{
CustomPrincipal principal = Thread.CurrentPrincipal as CustomPrincipal;
if(principal.IsInRole("operator"))
{
// Если роль пользователя — operator, скрываем функциональность.
btnEditUsers.Enabled = false;
btnViewUsers.Enabled = false;
}
if(principal.IsInRole("manager"))
{
// Для роли пользователя manager скрываем возможности изменения роли пользователя.
btnEditUsers.Enabled = false;
}
}
private void toolBar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
// Проверяем, какая кнопка была нажата, и
// выполняем соответствующее действие.
switch(e.Button.Tag.ToString())
{
case "view":
this.ViewUsers();
break;
case "edit":
this.EditUser();
break;
}
}
/// <summary>
/// Выводим пользователей на экран.
/// </summary>
private void ViewUsers()
{
lstViewUsers.Items.Clear();
XmlTextReader xmlReader = new XmlTextReader("Users.xml");
xmlReader.WhitespaceHandling = WhitespaceHandling.None;
while(xmlReader.Read())
{
// Считываем данные из XML-файла, представляем их в виде
// строки элемента управления ListView
if(xmlReader["name"] == null)
continue;
ListViewItem item = new ListViewItem(new
string[]{xmlReader["name"], xmlReader["role"], xmlReader["id"]}, 0);
lstViewUsers.Items.Add(item);
}
xmlReader.Close();
}
/// <summary>
/// Изменяем выбранного пользователя.
/// </summary>
private void EditUser()
{
if(lstViewUsers.SelectedItems.Count == 0)
{
MessageBox.Show("Вы дожны выбрать пользователя.", "Сообщение приложения");
}
else
{
string userName = lstViewUsers.SelectedItems[0].Text;
EditUserForm edit = new EditUserForm(userName);
if(edit.ShowDialog() != DialogResult.OK)
return;
// Считываем новую роль.
string newRole = edit.cmbRole.SelectedItem.ToString();
// Обновляем файл XML новыми данными.
XmlDocument doc = new XmlDocument();
StreamReader reader = new StreamReader("Users.xml");
doc.LoadXml(reader.ReadToEnd());
reader.Close();
XmlElement root = doc.DocumentElement;
foreach(XmlNode child in root.ChildNodes)
{
if(child.Attributes["name"].Value == userName)
{
child.Attributes["role"].Value = newRole;
break;
}
}
doc.Save("Users.xml");
this.ViewUsers();
}
}
}
}
При запуске приложения доступ к главной форме будет блокироваться до прохождения авторизации (рис.рис. 10.16).
(рис 10.16) Авторизация пользователяДобавьте новую Windows-форму в проект и назовите ее LoginForm.cs. Листинг этой формы:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace CustomSecurity
{
/// <summary>
/// Форма ввода имени пользователя.
/// </summary>
public class LogInForm : System.Windows.Forms.Form
{
// Обратите внимание на то, что модификатору доступа элемента
// управления txbUserName установлено значение public. Это сделано для того, чтобы
// можно было из другой формы получить введенный в него текст.
public System.Windows.Forms.TextBox txbUserName;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnOk;
private System.Windows.Forms.Label lblUserName;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public LogInForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.txbUserName = new System.Windows.Forms.TextBox();
this.btnCancel = new System.Windows.Forms.Button();
this.btnOk = new System.Windows.Forms.Button();
this.lblUserName = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// txbUserName
//
this.txbUserName.Location = new System.Drawing.Point(16, 32);
this.txbUserName.Name = "txbUserName";
this.txbUserName.Size = new System.Drawing.Size(312, 20);
this.txbUserName.TabIndex = 0;
this.txbUserName.Text = "";
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(152, 80);
this.btnCancel.Name = "btnCancel";
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Отмена";
//
// btnOk
//
this.btnOk.Location = new System.Drawing.Point(248, 80);
this.btnOk.Name = "btnOk";
this.btnOk.TabIndex = 2;
this.btnOk.Text = "ОК";
this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
//
// lblUserName
//
this.lblUserName.Location = new System.Drawing.Point(16, 8);
this.lblUserName.Name = "lblUserName";
this.lblUserName.Size = new System.Drawing.Size(216, 24);
this.lblUserName.TabIndex = 3;
this.lblUserName.Text = "Введите имя пользователя:";
//
// LogInForm
//
this.AcceptButton = this.btnOk;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(342, 116);
this.Controls.Add(this.lblUserName);
this.Controls.Add(this.btnOk);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.txbUserName);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "LogInForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Авторизация";
this.ResumeLayout(false);
}
#endregion
private void btnOk_Click(object sender, System.EventArgs e)
{
// Устанавливаем положительный результат выполнения
// и закрываем форму.
this.DialogResult = DialogResult.OK;
this.Close();
}
}
}
Последняя форма, которая будет использована в нашем проекте, — EditUserForm, изменяющая роль пользователей (рис. 10.17).
(рис 10.17) Форма EditUserFormЛистинг этой формы:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace CustomSecurity
{
/// <summary>
/// Форма изменения роли пользователя
/// </summary>
public class EditUserForm : System.Windows.Forms.Form
{
private System.Windows.Forms.Label lblName;
private System.Windows.Forms.Label lblRole;
public System.Windows.Forms.ComboBox cmbRole;
private System.Windows.Forms.Label lblUserName;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnEdit;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private string UserName;
// Обратите внимание на то, что у формы переопределен конструктор.
// Это сделано для передачи форме имени пользователя,
// роль которого необходимо изменить.
public EditUserForm(string userName)
{
UserName = userName;
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support — do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(EditUserForm));
this.lblName = new System.Windows.Forms.Label();
this.lblRole = new System.Windows.Forms.Label();
this.btnCancel = new System.Windows.Forms.Button();
this.btnEdit = new System.Windows.Forms.Button();
this.cmbRole = new System.Windows.Forms.ComboBox();
this.lblUserName = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// lblName
//
this.lblName.Location = new System.Drawing.Point(16, 16);
this.lblName.Name = "lblName";
this.lblName.Size = new System.Drawing.Size(144, 23);
this.lblName.TabIndex = 1;
this.lblName.Text = "Имя пользователя";
//
// lblRole
//
this.lblRole.Location = new System.Drawing.Point(16, 48);
this.lblRole.Name = "lblRole";
this.lblRole.Size = new System.Drawing.Size(144, 23);
this.lblRole.TabIndex = 3;
this.lblRole.Text = "Роль пользователя";
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(192, 112);
this.btnCancel.Name = "btnCancel";
this.btnCancel.TabIndex = 4;
this.btnCancel.Text = "Отмена";
//
// btnEdit
//
this.btnEdit.Location = new System.Drawing.Point(72, 112);
this.btnEdit.Name = "btnEdit";
this.btnEdit.TabIndex = 5;
this.btnEdit.Text = "Изменить";
this.btnEdit.Click += new System.EventHandler(this.button2_Click);
//
// cmbRole
//
this.cmbRole.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbRole.Items.AddRange(new object[] {
"admin",
"manager",
"operator"});
this.cmbRole.Location = new System.Drawing.Point(176, 48);
this.cmbRole.Name = "cmbRole";
this.cmbRole.Size = new System.Drawing.Size(200, 21);
this.cmbRole.TabIndex = 6;
//
// lblUserName
//
this.lblUserName.Location = new System.Drawing.Point(176, 16);
this.lblUserName.Name = "lblUserName";
this.lblUserName.Size = new System.Drawing.Size(200, 23);
this.lblUserName.TabIndex = 7;
//
// EditUserForm
//
this.AcceptButton = this.btnEdit;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(386, 152);
this.Controls.Add(this.lblUserName);
this.Controls.Add(this.cmbRole);
this.Controls.Add(this.btnEdit);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.lblRole);
this.Controls.Add(this.lblName);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "EditUserForm";
this.Text = "Изменение пользователя";
this.Load += new System.EventHandler(this.EditUserForm_Load);
this.ResumeLayout(false);
}
#endregion
private void button2_Click(object sender, System.EventArgs e)
{
// Устанавливаем положительный результат выполнения
// и закрываем форму.
this.DialogResult = DialogResult.OK;
this.Close();
}
private void EditUserForm_Load(object sender, System.EventArgs e)
{
// При загрузке формы устанавливаем имя пользователя в
//элемент управления lblUserName
lblUserName.Text = UserName;
}
}
}
Запустите приложение. При загрузке проверяется имя пользователя и инициализируется его роль. В зависимости от роли в приложении интерфейс изменяется следующим образом:
На диске, прилагаемом к книге, вы найдете приложение CustomSecurity (Code\Glava10\ CustomSecurity).
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.