|
При нажатии на Сумма в примере 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.
