RUS_-_Karejev_-_Razrabotka_Windows-prilozhenij
.pdf
//Создаем и инициализируем именем пользователя
//объект класса 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.3.
Если пользователь проходит проверку, он получает дальнейший доступ (рис. 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.4.
При запуске этого приложения дважды осуществляется авторизация – по имени пользователя (личности) и его роли (рис. 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).
Приложение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.
Теперь займемся пользовательским интерфейсом программы и привязкой обработчиков. Все приложения, которые мы рассматривали в предыдущих лекциях, я сопровождал описанием дизайна форм и не указывал в листинге область 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.7.
При запуске приложения доступ к главной форме будет блокироваться до прохождения авторизации (рис.рис. 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();
}
}
}
Листинг 10.8.
Последняя форма, которая будет использована в нашем проекте, — 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
//
