При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан. Затем: Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз. |
Безопасность Windows-форм
Контроль доступа приложения
После создания объекта роли к нему можно добавить код для реализации контроля доступа, основанного на ролях пользователей. Рассмотрим пример, в котором осуществляется контроль доступа по имени пользователя, причем сравнение строк не чувствительно к регистру. Создайте новое консольное приложение и назовите его 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.4.
При запуске этого приложения дважды осуществляется авторизация – по имени пользователя (личности) и его роли (рис. 10.14):
Для объектов класса WindowsIdentity имя пользователя представлено в виде имени пользователя и домена. Например, если бы приведенный выше пример использовал объект класса WindowsIdentity, то имя для сравнения было бы следующим: DOMAIN\Manager.
Для объектов класса WindowsPrincipal роль включает в себя и имя домена. Ниже представлен пример проверки роль объектом класса WindowsPrincipal:
if(WindowsPrincipalObj.IsInRole("DOMAIN\\ Manager ")) // Разрешить действие
Если учесть, что ваше приложение может кочевать по сети в поисках нового пользователя, то строго прописывать имя домена не рекомендуется. Вместо этого для проверки ролей пользователей объектом класса WindowsPrincipal нужно указывать не строку с названием роли, а член перечисления WindowsBuiltInRole, как показано ниже:
MyPrincipal.IsInRole(WindowsBuiltInRole. Manager);
На диске, прилагаемом к книге, вы найдете приложение AccessControl (Code\Glava10\ AccessControl).
Приложение CustomSecurity — использование собственной технологии ролевой безопасности
Мы обсудили теоретические основы безопасности .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)); } } }Листинг 10.5.
Листинг 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)); } } }Листинг 10.6.