В прошлой статье Авторизация на Delphi мы рассмотрели один из способов организации появления окна авторизации пользователя. Мы рассматривали этот вопрос в отношении приложения многодокументного типа. В этой статье акцент будет сделан на однодокументное приложение SDI типа.
Что такое SDI и MDI типы приложений?
SDI-приложение — это приложение однодокументного типа (Single document interface). Это означает, что все окна приложения не принадлежат какому-то общему окну, а открываются сами по себе и в диспетчере задач Windows каждое открытое окно будет высвечиваться на своей вкладке. Примером такого типа приложений является MS Word, MS Excel и другие программы из пакета MS Office.
Здесь каждый созданный файл (документ Word или книга Excel) будут открываться в отдельном окне и выглядеть при этом как отдельно открытый экземпляр приложения, хотя на самом деле это и не так. В самых ранних версиях эти приложения была MDI типа.
MDI-приложение — это многодокументный тип приложения (multiple document interface). Интерфейс MDI типа подразумевает одно главное окно приложения, в котором будут открываться все остальные окна. Примером такого приложения может служить CorelDraw или Adobe Photoshop, Adobe Reader...
Схема авторизации пользователя
Наверное с этого следовало бы начать в предыдущей статье об авторизации. Но я думаю, что мы посмотрим на один из вариантов схемы в этой. Ничего страшного, давайте приступим.
Итак, авторизация может быть организована следующим образом. Все пользователи должны быть где то учтены. очень удобно для этих целей использовать базу данных. Да и вообще, если приложение разрабатывается для работы с базами данных (для чего чаще всего и используется Delphi), то тогда на самом деле стоит хранить всю информацию о пользователях именно в базе данных.
Так как я люблю работать с MySQL я буду показывать скрины на его примере. Но на других СУБД, таких как MS SQL Server, MS Access и других способ организации точно такой же.
Итак, для хранения информации о пользователях можно использовать одну таблицу. Но мы немного усложним задачу, чтобы сделать ее интересней. Для хранения информации об аккаунтах, мы будем использовать три таблицы:
- Роли. В этой таблице мы будем хранить информацию о ролях пользователей;
- Пользователи. В данной таблице будет храниться информация обо всех пользователях, которым будет открываться доступ к программе;
- Аккаунты. Данная таблица будет содержать в себе открытые аккаунты (личные кабинеты, учетные записи). Для чего нужна эта таблица? Все очень просто. Один и тот же пользователь может иметь более одной учетной записи. Например, он может быть администратором и каким то другим специалистом.
Тогда схема таблиц для авторизации пользователей будет выглядеть так:

Мы видим две родительские таблицы (Роли и Пользователи) и одну дочернюю — Аккаунты. Естественно, что связь между Роли и Аккаунты, а также Пользователи и Аккаунты будет как один-ко-многим. Почему? Потому что один и тот же пользователь, как я говорил выше в нашем примере может иметь несколько ролей. Ровно как и одна и та же роль может быть назначена разным пользователям. Это называется отношение много-ко многим.
Отношение много-ко-многим присутствует между таблицами Роли и Пользователи. Но такое отношение не назначается напрямую, а решается с помощью промежуточной (объединяющей таблицы). В нашем случае это таблица Аккаунты.
Теперь давайте вернемся к непосредственной авторизации на Delphi и рассмотрим вопрос о том, как сделать авторизацию пользователя.
Форма авторизации пользователя
В окне дизайнера форм данная форма может выглядеть так, как показано на рисунке ниже:

Обычно модуль, описывающий форму авторизации пользователя я называю login.pas, форму именую как frmLogin. Задавать корректные имена очень удобно. Не следует оставлять элементы с именами по умолчанию.
Также в этой форме присутствует раскрывающийся список (TCombobox) и текстовое поле (TEdit), две кнопки класса TPanel и один компонент доступа к данным типа (TadoQuery).
В данном примере кнопки представлены компонентом TPanel, а не TButton. Это сделано для того, чтобы мне было легче придать интерфейсу тот вид, которого я хотел добиться. Эти вещи можно сделать и по другому, а именно можно использовать стили и тогда можно использовать обычные кнопки, но назначать им стили отображения. Это другой способ. Но это не тема данной статьи, а небольшое отступление, поэтому больше здесь мы это затрагивать не будем.
Собственно весь дальнейший процесс описан в предыдущей статье Авторизация на Delphi. Здесь я покажу только то, как заполняется список из логинов, потому что в предыдущей статье я не описывал это...
Для заполнения списка логинами я использую компонент набора данных TadoQuery с именем adoLogin.
В свойстве SQL данного компонента я использую запрос:
SELECT роли.Код_роли, роли.Роль, пользователи.Код_пользователя, пользователи.ФИО, аккаунты.Код_аккаунта, аккаунты.Логин, аккаунты.Пароль FROM роли INNER JOIN аккаунты ON аккаунты.Код_роли = роли.Код_роли INNER JOIN пользователи ON аккаунты.Код_пользователя = пользователи.Код_пользователя WHERE аккаунты.Статус = 'Действующий'
В событии onCreate (когда у нас создается форма для авторизации пользователя) мы пишем следующий код:
procedure TfrmLogin.FormCreate(Sender: TObject); var KolZap: integer; TekZap: integer; i: integer; // Счетчик begin //Прячем все пункты меню frmMDI.mnuAdmin.Visible:=false; frmMDI.mnuLogin.Visible:=false; frmMDI.mnuTovar.Visible:=false; frmMDI.mnuKont.Visible:=false; frmMDI.mnuDv.Visible:=false; frmMDI.mnuAnalis.Visible:=false; //Подключаемся к Таблице Логины adoLogin.Open; //Заполняем логинами раскрывающийся список cboLogin.Clear; KolZap:=adoLogin.RecordCount; adoLogin.First; TekZap:=adoLogin.RecNo; for i:=TekZap to KolZap do begin cboLogin.Items.Add(adoLogin.fieldbyname('Логин').AsString); adoLogin.Next; end; //Сортируем список cboLogin.Sorted:=true; //Ставим первое значение в списке по умолчанию cboLogin.ItemIndex:=0; end;
Теперь после запуска приложения и появления окна авторизации раскрывающийся список Имя пользователя будет заполнен логинами из набора данных, полученного на основе запроса на объединение данных из трех таблиц (Роли, Пользователи и Аккаунты).
В кнопке Вход нужно прописать такой код:
procedure TfrmLogin.pnlFirstGroupClick(Sender: TObject); var sTiplist: TStrings; sTiplistUser: String; sPassword: string; begin sLogin:=cboLogin.Text; sPassword:=trim(txtPassword.Text); //Теперь проверяем пароль пользователя adoLogin.First;//Ставим указатель на первую запись таблицы adoLogin.Locate('Логин', sLogin,[]);// Находим в таблице запись с выбранным логином if adoLogin.fieldbyname('Пароль').AsString = sPassword then //Если пароль совпадает с введенным, то begin //Заменяем пробелы на подчеркивание, потому что в строковом перечислении //пробелы не работают пробелы sTipUser:=adoLogin.fieldbyname('Роль').AsString; sTiplistUser:=StringReplace(adoLogin.fieldbyname('Роль').AsString, ' ', '_',[rfReplaceAll, rfIgnoreCase]); sTiplist:=TStrings(GetEnumValue(TypeInfo(TStrings), sTiplistUser)); case sTiplist of Администраторы: begin frmMDI.mnuAdmin.Visible:=true; frmMDI.mnuLogin.Visible:=true; frmMDI.mnuTovar.Visible:=true; frmMDI.mnuKont.Visible:=true; frmMDI.mnuDv.Visible:=true; frmMDI.mnuAnalis.Visible:=true; end; Менеджеры: begin frmMDI.mnuAdmin.Visible:=false; frmMDI.mnuLogin.Visible:=true; frmMDI.mnuTovar.Visible:=true; frmMDI.mnuKont.Visible:=true; frmMDI.mnuDv.Visible:=true; frmMDI.mnuAnalis.Visible:=true; end; Кладовщики: begin frmMDI.mnuAdmin.Visible:=false; frmMDI.mnuLogin.Visible:=true; frmMDI.mnuTovar.Visible:=false; frmMDI.mnuKont.Visible:=false; frmMDI.mnuDv.Visible:=true; frmMDI.mnuAnalis.Visible:=false; end; end; //Раз пароль подошел, то получаем из записи остальные //характеристики пользователя iIDUser:=adoLogin.fieldbyname('Код_аккаунта').AsInteger; sFIOUser:=adoLogin.fieldbyname('ФИО').AsString; //Присваиваем ФИО пользователя, вошедшего в систему из набора //Устанавливаем для главной формы заголовок frmMDI.Caption:='Мониторинг складского учета торговой сети - '+'Пользователь: '+sLogin+' '+'ФИО: '+sFIOUser+' '+'Роль: '+sTipUser; frmMDI.AlphaBlendValue:=255;//Делаем главную форму видимой Close;//и закрываем окно авторизации end else //Если введенный пароль не совпал с тем, который в записи, то begin MessageCreate(Left,Top,Height,Width,'Пароль не верный! Обратитесь к администратору системы!'); end; end;
Здесь MessageCreate — это моя пользовательская процедура создания окна сообщения. Вы можете вместо нее использовать, например стандартную функцию MessageBox или свою собственную форму, разработанную в Delphi.
Это был все также пример для MDI-приложения. Его я показал для того, что бы рассмотреть идею общей организации схемы авторизации пользователя на Delphi.
Хочу также отметить, что в данном примере всех пользователей создает администратор. Он же и назначает пользовательский пароль. Здесь пользователь не создает учетную запись самостоятельно. Это достаточно распространенный метод в бизнес-приложениях. После создания учетной записи, администратор отдает новому пользователю парольную карточку.
Форма авторизации пользователя в SDI-приложении на Delphi
Все, что будет написано ниже справедливо как для библиотеки VCL, так и для кроссплатформенной библиотеки FMX.
В автозапуске оставляем только основную форму. Допустим называется она frmSDI. Форму frmLogin убираем из автозапуска. В форме frmSDI объявляем глобальную переменную: zapusk: boolean;
В событии OnCreate формы frmSDI этой переменной присваиваем True.
procedure TfrmSDI.FormCreate(Sender: TObject); begin zapusk:=true; end;
В событии OnShow формы frmSDI пишем:
procedure TfrmSDI.FormShow(Sender: TObject); begin if Zapusk=true then begin Application.CreateForm(TfrmLogin, frmLogin); frmLogin.Show; frmSDI.Hide; end; end;
В событии OnCreate формы frmLogin пишем:
procedure TfrmLogin.FormCreate(Sender: TObject); begin Zapusk:=false; end;
В событии OnDestroy формы frmLogin пишем:
procedure TfrmLogin.FormDestroy(Sender: TObject); begin zapusk:=true; end;
Сама же форма авторизации пользователя (frmLogin) , будет точно такой же, как и в случае с приложением MDI-типа. Итак, мы рассмотрели некоторые вопросы по созданию формы авторизации пользователей на Delphi. Надеюсь, что материал будет полезным для вас.