Безопасность: аутентификация с помощью форм
Упражнение 5. Административная страница создания пользователей в БД
Для создания и удаления пользователей следует применить перегруженные статические методы CreateUser() и DeleteUser() класса Membership. Нужно создать административные страницы, содержащие требуемые поля для ввода информации. Поля можно контролировать валидаторами, хотя это и необязательно, поскольку эти страницы административные, пользоваться такими страницами будут администраторы, которые будут строго соблюдать правила ввода.
Метод CreateUser() имеет несколько перегрузок, из которых простейшая принимает только имя и пароль. В то же время более полные перегрузки требуют ввода контрольного вопроса и ответа на него. В самой полной перегрузке метод CreateUser() содержит экземпляр перечисления MembershipCreateStatus в качестве выходного параметра, позволяющий проанализировать причину ошибки в случае ее возникновения. По умолчанию требуется применять наиболее полную перегрузку метода CreateUser(), включающую контрольный вопрос и ответ, чтобы избежать выброса исключения MembershipCreateUserException.
Удаление пользователя выполняется методом Membership.DeleteUser(string), которому передается в качестве параметра уникальное имя пользователя. Пользователь безвозвратно удаляется из всех таблиц вместе со всей связанной с ним информацией.
В качестве примера создадим собственную страницу добавления, имеющую аналогичную функциональность с WAT, и отдельно страницу удаления пользователя в БД. Для простоты эти страницы мы переделаем из страницы регистрации MyLogin.aspx.
- Скопируйте страницу MyLogin.aspx и присвойте копии имя AddUserMembership.aspx
Провайдер базы данных не добавляет новых пользователей, если пароль имеет длину менее 7 символов и если среди них нет хотя-бы одного неалфавитно-цифрового символа. Для проверки выполнения этого условия, а также для тренировки, закрепим за полем пароля кодируемый элемент проверки достоверности CustomValidator. С помощью этого элемента реализуем проверку достоверности на клиенте и на сервере.
Неалфавитно-цифровые символы представим в виде ASCII -кодов согласно следующей таблице
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | ||||||||||
1 | ||||||||||
2 | ||||||||||
3 | ! | " | # | $ | % | & | ' | |||
4 | ( | ) | * | + | , | . | / | |||
5 | : | ; | ||||||||
6 | < | = | > | ? | @ | |||||
7 | ||||||||||
8 | ||||||||||
9 | [ | \ | ] | ^ | _ | |||||
10 | ||||||||||
11 | ||||||||||
12 | { | | | } | ~ |
Строка ASCII -кодов неалфавитно-цифровых символов на JavaScript будет такой:
var nonAlphanumericCharactersString = String.fromCharCode(33,34,35,36,37,38,39,40,41,42,43,44,45,46, 58,59,60,61,62,63,64,91,92,93,94,95,123,124,125,126);
- Переделайте интерфейсную и скриптовую части страницы, чтобы она стала такой
<%@ Page Language="C#" EnableViewState="false" %> <script runat="server"> // Блок на C# по умолчанию protected void Page_Load(object sender, EventArgs e) { // Вынесли из интерфейсной части из-за большой ширины листинга passwordValidator.ErrorMessage = "Пароль должен иметь длину не менее 7 символов" + " и содержать хотя бы один неалфавитно-цифровой символ"; } protected void AddUserMembership_Click(object sender, EventArgs e) { this.Validate();// Исполнить валидаторы на сервере if (!this.IsValid)// Оценить флаг достоверности return;// Отправить назад как есть // Создаем экземпляр перечисления MembershipCreateStatus status; // Попытка добавить try { Membership.CreateUser( username.Text, password.Text, email.Text, question.Text, answer.Text, true, out status ); lblResult.Text = String.Empty; switch (status) { case MembershipCreateStatus.DuplicateUserName: lblResult.Text = "Дублированное имя пользователя<br/>"; goto default; case MembershipCreateStatus.ProviderError: lblResult.Text = "Ошибка Поставщика<br/>"; goto default; default: // Дальше лень анализировать lblResult.Text += "Пользователь не создан"; lblResult.ForeColor = System.Drawing.Color.Red; break; case MembershipCreateStatus.Success: // Выдаем успешную информацию lblResult.Text = "Пользователь " + username.Text + " создан успешно!"; // Рекурсивно очищаем все текстовые поля ввода EmptyTextBox(form1.Controls); lblResult.Text += "<br/>Поля ввода очищены"; break; } } // Откат catch (MembershipCreateUserException ex) { lblResult.Text = "<h2 style='color: Red'>Ошибка создания пользователя</h2>"; System.Diagnostics.Debug.WriteLine("Exception: " + ex.Message); } } private void EmptyTextBox(ControlCollection controls) { foreach (Control ctrl in controls) { if (ctrl is TextBox) ((TextBox)ctrl).Text = String.Empty; // Идем в глубину иерархической цепочки и ищем дочерние элементы if (ctrl.Controls != null) EmptyTextBox(ctrl.Controls); } } </script> <script language="C#" runat="server"> // Еще один блок с явным указанием C#, хотя можно атрибут языка и не указывать // Проверка допустимости пароля на сервере // Обработчик события ServerValidate элемента CustomValidator protected void passwordValidator_ServerValidate(object source, ServerValidateEventArgs args) { string data = args.Value; // Установить начальное значение флага достоверности args.IsValid = false; // Проверить длину пароля if (data.Length < 7) return; // Проверить наличие неалфавитноцифрового символа // Формируем массив неалфавитноцифровых символов по их кодам, // чтобы не связываться с интерпретируемыми символами int[] nonAlphanumericCharactersCode = {33,34,35,36,37,38,39,40,41,42,43,44,45,46, 58,59,60,61,62,63,64,91,92,93,94,95,123,124,125,126}; // Перебираем символы пользовательского ввода foreach (char ch in data) { for (int i = 0; i < nonAlphanumericCharactersCode.Length; i++) if (ch == Convert.ToChar(nonAlphanumericCharactersCode[i])) { // Пароль допустим args.IsValid = true; return; } } } </script> <script language="javascript" type="text/javascript"> // Проверка допустимости пароля на клиенте. Работает при включенном // по умолчанию свойстве EnableClientScript элемента CustomValidator function ValidatePassword(source, args) { // Расщепить строку на массив символов с пустым разделителем var data = args.Value.split(""); // Установить начальное значение флага достоверности args.IsValid = false; // Проверить длину пароля if(data.length < 7) return; // Проверить наличие неалфавитноцифрового символа // Формируем строку неалфавитноцифровых символов по их кодам, // чтобы не связываться с интерпретируемыми символами var nonAlphanumericCharactersString = String.fromCharCode(33,34,35,36,37,38,39,40,41,42,43,44,45,46, 58,59,60,61,62,63,64,91,92,93,94,95,123,124,125,126); for(var ch in data) { if(nonAlphanumericCharactersString.indexOf(data[ch]) != -1) { // Пароль допустим args.IsValid = true; return; } } } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Добавление пользователей в БД</title> </head> <body> <form id="form1" runat="server"> <div style="text-align: center"> <h2 id="H2_1" runat="server"> Введите учетную запись для добавления пользователя</h2> <asp:Panel ID="MainPanel" runat="server" BorderColor="Silver" BorderStyle="Ridge" BorderWidth="2px" Height="90px" Width="364px"> <table cellpadding="5" style="width: 137%"> <tr> <td> </td> <td align="right" height="43" style="width: 224px"> Имя пользователя: </td> <td> <asp:TextBox ID="username" runat="server" /> </td> <td> <asp:RequiredFieldValidator ID="usernameRequiredValidator" runat="server" ControlToValidate="username" ErrorMessage='Не заполнено поле "Имя пользователя"' Display="Dynamic">*</asp:RequiredFieldValidator> </td> </tr> <tr> <td> </td> <td align="right" height="43" style="width: 224px"> Пароль: </td> <td> <asp:TextBox ID="password" runat="server" /> </td> <td> <asp:RequiredFieldValidator ID="passwordRequiredValidator" runat="server" ControlToValidate="password" ErrorMessage='Не заполнено поле "Пароль"' Display="Dynamic">*</asp:RequiredFieldValidator> <asp:CustomValidator ID="passwordValidator" runat="server" ControlToValidate="password" Display="Dynamic" ClientValidationFunction="ValidatePassword" OnServerValidate="passwordValidator_ServerValidate">*</asp:CustomValidator> </td> </tr> <tr> <td> </td> <td align="right" height="43" style="width: 224px"> E-mail:</td> <td> <asp:TextBox ID="email" runat="server"></asp:TextBox> </td> <td> <asp:RequiredFieldValidator ID="emailRequiredValidator" runat="server" ControlToValidate="email" ErrorMessage='Не заполнено поле "E-mail"' Display="Dynamic">*</asp:RequiredFieldValidator> <asp:RegularExpressionValidator ID="emailValidator" runat="server" ControlToValidate="email" ErrorMessage="Неверный E-mail" ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" Display="Dynamic">*</asp:RegularExpressionValidator> </td> </tr> <tr> <td> </td> <td align="right" height="43" style="width: 224px"> Контрольный вопрос:</td> <td> <asp:TextBox ID="question" runat="server"></asp:TextBox> </td> <td> <asp:RequiredFieldValidator ID="questionRequiredValidator" runat="server" ControlToValidate="question" ErrorMessage='Не заполнено поле "Контрольный вопрос"' Display="Dynamic">*</asp:RequiredFieldValidator> </td> </tr> <tr> <td> </td> <td align="right" height="43" style="width: 224px"> Контрольный ответ:</td> <td> <asp:TextBox ID="answer" runat="server"></asp:TextBox> </td> <td> <asp:RequiredFieldValidator ID="answerRequiredValidator" runat="server" ControlToValidate="answer" ErrorMessage='Не заполнено поле "Контрольный ответ"' Display="Dynamic">*</asp:RequiredFieldValidator> </td> </tr> <tr> <td colspan="4" align="center"> <asp:Button ID="btnAddUser" runat="server" Text="Добавить" OnClick="AddUserMembership_Click" /> </td> </tr> </table> </asp:Panel> <asp:Label ID="lblResult" runat="server" Font-Bold="True" Font-Size="Large" ForeColor="Blue" /> </div> <asp:ValidationSummary ID="ValidationSummary1" runat="server" /> </form> </body> </html>Листинг 37.16. Административная страница AddUserMembership.aspx добавления пользователей в БД
- Выполните страницу AddUserMembership.aspx и убедитесь в работоспособности рассматриваемого механизма добавления пользователей в БД SQL Server
Упражнение 6. Административная страница удаления пользователей из БД
Теперь рассмотрим вопрос удаления пользователей. Он намного проще, чем создание. Нужно только применить метод System.Web.Security.Membership.DeleteUser(), который имеет две перегрузки:
- public static bool DeleteUser( string username ) - удаляет по имени пользователя все связанные с ним данные из всех таблиц БД
- public static bool DeleteUser( string username, bool deleteAllRelatedData ) - удаляет пользователя только из таблицы aspnet_Users, если флаг равен false. Иначе удаляет все связанные с пользователем данные при флаге равном true
Обе перегрузки возвращают true, если пользователь успешно удален, иначе false.
- Разработайте из копии страницы MyLogin.aspx административную страницу удаления пользователей DeleteUserMembership.aspx, которая будет примерно такой
<%@ Page Language="C#" EnableViewState="false" %> <script runat="server"> protected void DeleteUser_Click(object sender, EventArgs e) { if (Membership.DeleteUser(username.Text)) { lblResult.Text="Пользователь " + username.Text + " успешно удален"; username.Text = ""; } else { lblResult.Text = "Пользователь не удален"; lblResult.ForeColor = System.Drawing.Color.Red; } } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Удаление пользователей из БД</title> </head> <body> <form id="form1" runat="server"> <div style="text-align: center"> <h2> Введите имя удаляемого пользователя</h2> <asp:Panel ID="MainPanel" runat="server" BorderColor="Silver" BorderStyle="Ridge" BorderWidth="2px" Height="90px" Width="412px"> <table cellpadding="5" style="width: 100%"> <tr> <td align="right" height="43" style="width: 167px"> Имя пользователя:</td> <td> <asp:TextBox ID="username" runat="server" /> </td> </tr> </table> <asp:Button ID="Button1" runat="server" Text="Удалить" OnClick="DeleteUser_Click" /></asp:Panel> <asp:Label ID="lblResult" runat="server" Font-Bold="True" Font-Size="Large" ForeColor="Blue"> </asp:Label> </div> </form> </body> </html>Листинг 37.16. Админ. страница DeleteUserMembership.aspx удаления пользователей из БД
- Испытайте страницу удаления пользователей