Пошаговое руководство. Реализация пользовательской проверки подлинности и авторизации
Обновлен: Ноябрь 2007
В этом пошаговом руководстве демонстрируется реализация пользовательской проверки подлинности и авторизации с помощью классов, производных от IIdentity и IPrincipal. Также демонстрируется переопределение заданного по умолчанию удостоверения потока приложения (удостоверения Windows) путем присвоения объекту My.User.CurrentPrincipal значения экземпляра класса, производного от IPrincipal. Информация о новом пользователе доступна немедленно через объект My.User, который возвращает сведения об удостоверении текущего пользователя потока.
Деловые приложения часто обеспечивают доступ к данным или ресурсам на основании учетных данных, предоставляемых пользователем. Обычно такие приложения проверяют роль пользователя и в соответствии с ней предоставляют доступ к ресурсам. Среда CLR обеспечивает поддержку авторизации на основе ролей с использованием учетных записей Windows или пользовательских удостоверений. Дополнительные сведения см. в разделе Безопасность на основе ролей.
Приступая к работе
Прежде всего, создайте проект с главной формой и формой входа в систему и настройте его на использование пользовательской проверки подлинности.
Создание примера приложения
Создайте новый проект Visual Basic, представляющий собой приложение Windows. Дополнительные сведения см. в разделе Практическое руководство. Создание проекта приложения Windows.
По умолчанию главной форме присваивается имя Form1.
В меню Проект выберите команду Добавить новый элемент.
Выберите шаблон Форма входа и нажмите кнопку Добавить.
По умолчанию форме входа присваивается имя LoginForm1.
В меню Проект выберите команду Добавить новый элемент.
Выберите шаблон Класс, измените имя на SampleIIdentity и нажмите кнопку Добавить.
В меню Проект выберите команду Добавить новый элемент.
Выберите шаблон Класс, измените имя на SampleIPrincipal и нажмите кнопку Добавить.
В меню Проект выберите команду имя приложения > Свойства.
В конструкторе проекта перейдите на вкладку Приложение.
В раскрывающемся списке Режим проверки подлинности выберите Определяется приложением.
Настройка главной формы
Переключитесь на Form1 в конструкторе форм.
Добавьте элемент Кнопка в форму Form1 из Области элементов.
Имя кнопки по умолчанию — Button1.
Измените текст кнопки на Проверить подлинность.
Из Панели элементов добавьте элемент управления Надпись в форму Form1.
Имя надписи по умолчанию — Label1.
Измените текст надписи на пустую строку.
Из Панели элементов добавьте элемент управления Надпись в форму Form1.
Имя надписи по умолчанию — Label2.
Измените текст надписи на пустую строку.
Дважды щелкните кнопку Button1, чтобы создать обработчик событий Click, а затем откройте редактор кода.
Добавьте следующий код в метод Button1_Click.
My.Forms.LoginForm1.ShowDialog() ' Check if the user was authenticated. If My.User.IsAuthenticated Then Me.Label1.Text = "Authenticated " & My.User.Name Else Me.Label1.Text = "User not authenticated" End If If My.User.IsInRole(ApplicationServices.BuiltInRole.Administrator) Then Me.Label2.Text = "User is an Administrator" Else Me.Label2.Text = "User is not an Administrator" End If
На данном этапе приложение уже можно запускать, но из-за отсутствия кода проверки подлинности оно не будет устанавливать подлинность пользователей. Добавление кода проверки подлинности рассматривается в следующем разделе.
Создание удостоверения
.NET Framework использует интерфейсы IIdentity и IPrincipal в качестве основы для проверки подлинности и авторизации. Реализовав данные интерфейсы, можно осуществлять в приложении пользовательскую проверку подлинности, как демонстрируют эти процедуры.
Создание класса, реализующего интерфейс IIdentity
Выберите файл SampleIIdentity.vb в Обозревателе решений.
Этот класс инкапсулирует удостоверение пользователя.
В строке после Public Class SampleIIdentity добавьте следующий код, задающий наследование от IIdentity.
Implements System.Security.Principal.IIdentity
После добавления кода и нажатия клавиши ВВОД редактор кода создает свойства-заглушки, которые необходимо реализовать.
Добавьте закрытые поля для хранения имени пользователя и значения, показывающего, выполнена ли проверка подлинности для данного пользователя.
Private nameValue As String Private authenticatedValue As Boolean Private roleValue As ApplicationServices.BuiltInRole
Введите следующий код в свойстве AuthenticationType.
Свойство AuthenticationType должно возвращать строку, указывающую выбранный в настоящее время механизм проверки подлинности.
В данном примере используется явно заданный механизм проверка подлинности, поэтому возвращается строка "Пользовательская проверка подлинности". Если бы данные проверки подлинности пользователя хранились в базе данных SQL Server, строка могла бы иметь значение "SqlDatabase".
Return "Custom Authentication"
Введите следующий код в свойстве IsAuthenticated.
Return authenticatedValue
Свойство IsAuthenticated должно возвращать значение, указывающее, прошел ли данный пользователь проверку подлинности.
Свойство Name должно возвращать имя пользователя, связанного с данным удостоверением.
Введите следующий код в свойстве Name.
Return nameValue
Создайте свойство, возвращающее роль пользователя.
Public ReadOnly Property Role() As ApplicationServices.BuiltInRole Get Return roleValue End Get End Property
Создайте метод Sub New, инициализирующий класс путем проверки подлинности пользователя и установки имени и роли пользователя по имени и паролю.
Этот метод вызывает метод IsValidNameAndPassword, чтобы определить, допустимо ли заданное сочетание имени пользователя и пароля.
Public Sub New(ByVal name As String, ByVal password As String) ' The name is not case sensitive, but the password is. If IsValidNameAndPassword(name, password) Then nameValue = name authenticatedValue = True roleValue = ApplicationServices.BuiltInRole.Administrator Else nameValue = "" authenticatedValue = False roleValue = ApplicationServices.BuiltInRole.Guest End If End Sub
Создайте метод с именем IsValidNameAndPassword, определяющий допустимость комбинации имени пользователя и пароля.
Примечание о безопасности. Алгоритм проверки подлинности должен обрабатывать пароли безопасным образом. Например, пароль не должен храниться в поле класса.
Не следует хранить пароли пользователей в системе: утечка этой информации сведет на нет всю безопасность. Можно хранить хэш пароля каждого пользователя. (Хэш-функция шифрует данные так, что входные данные не могут быть получены из выходных данных.) Пароль не может быть определен непосредственно из хэша пароля.
Однако пользователь-злоумышленник не пожалеть времени и создать словарь хэшей всех возможных паролей, а затем найти пароль по известному хэшу. Для защиты от атак этого типа следует добавить salt-строку к паролю прежде, чем хэшировать его, для создания криптографического хэша. Salt-строка — это дополнительные данные, уникальные для каждого пароля, которые делают невозможным предварительное вычисление словаря хэшей.
Чтобы защитить пароли от злоумышленников, следует хранить только хэши с salt-строками паролей, желательно на безопасном компьютере. Пользователю-злоумышленнику чрезвычайно сложно восстановить пароль из хэша с salt-строкой. В этом примере используются методы GetHashedPassword и GetSalt для загрузки хэшированного пароля пользователя и salt-строки.
Private Function IsValidNameAndPassword( _ ByVal username As String, _ ByVal password As String) _ As Boolean ' Look up the stored hashed password and salt for the username. Dim storedHashedPW As String = GetHashedPassword(username) Dim salt As String = GetSalt(username) 'Create the salted hash. Dim rawSalted As String = salt & Trim(password) Dim saltedPwBytes() As Byte = _ System.Text.Encoding.Unicode.GetBytes(rawSalted) Dim sha1 As New _ System.Security.Cryptography.SHA1CryptoServiceProvider Dim hashedPwBytes() As Byte = sha1.ComputeHash(saltedPwBytes) Dim hashedPw As String = Convert.ToBase64String(hashedPwBytes) ' Compare the hashed password with the stored password. Return hashedPw = storedHashedPW End Function
Создайте функции с именами GetHashedPassword и GetSalt, возвращающие хэшированный пароль и salt-строку для указанного пользователя.
Примечание о безопасности. Следует избегать жесткого кодирования хэшированных паролей и salt-строк в клиентских приложениях по двум причинам. Во-первых, пользователи-злоумышленники смогут получить доступ к ним и найти столкновение хэшей. Во-вторых, нельзя будет изменить или отозвать пароль пользователя. Приложение должно получать хэшированный пароль и salt-строку для данного пользователя из безопасного источника, который обслуживается администратором.
Хотя для простоты в этом примере используются жестко закодированные хэшированный пароль и salt-строка, в рабочем коде следует использовать более безопасный подход. Например можно хранить информацию о пользователе в базе данных SQL Server и получать доступ к ней с помощью хранимых процедур. Дополнительные сведения см. в разделе Практическое руководство. Подключение к данным в базе данных.
Примечание. Пароль, соответствующий этому жестко закодированному хэшированному паролю, дан в разделе "Проверка приложения".
Private Function GetHashedPassword(ByVal username As String) As String ' Code that gets the user's hashed password goes here. ' This example uses a hard-coded hashed passcode. ' In general, the hashed passcode should be stored ' outside of the application. If Trim(username).ToLower = "testuser" Then Return "ZFFzgfsGjgtmExzWBRmZI5S4w6o=" Else Return "" End If End Function Private Function GetSalt(ByVal username As String) As String ' Code that gets the user's salt goes here. ' This example uses a hard-coded salt. ' In general, the salt should be stored ' outside of the application. If Trim(username).ToLower = "testuser" Then Return "Should be a different random value for each user" Else Return "" End If End Function
Файл SampleIIdentity.vb теперь должен содержать следующий код:
Public Class SampleIIdentity
Implements System.Security.Principal.IIdentity
Private nameValue As String
Private authenticatedValue As Boolean
Private roleValue As ApplicationServices.BuiltInRole
Public ReadOnly Property AuthenticationType() As String Implements System.Security.Principal.IIdentity.AuthenticationType
Get
Return "Custom Authentication"
End Get
End Property
Public ReadOnly Property IsAuthenticated() As Boolean Implements System.Security.Principal.IIdentity.IsAuthenticated
Get
Return authenticatedValue
End Get
End Property
Public ReadOnly Property Name() As String Implements System.Security.Principal.IIdentity.Name
Get
Return nameValue
End Get
End Property
Public ReadOnly Property Role() As ApplicationServices.BuiltInRole
Get
Return roleValue
End Get
End Property
Public Sub New(ByVal name As String, ByVal password As String)
' The name is not case sensitive, but the password is.
If IsValidNameAndPassword(name, password) Then
nameValue = name
authenticatedValue = True
roleValue = ApplicationServices.BuiltInRole.Administrator
Else
nameValue = ""
authenticatedValue = False
roleValue = ApplicationServices.BuiltInRole.Guest
End If
End Sub
Private Function IsValidNameAndPassword( _
ByVal username As String, _
ByVal password As String) _
As Boolean
' Look up the stored hashed password and salt for the username.
Dim storedHashedPW As String = GetHashedPassword(username)
Dim salt As String = GetSalt(username)
'Create the salted hash.
Dim rawSalted As String = salt & Trim(password)
Dim saltedPwBytes() As Byte = _
System.Text.Encoding.Unicode.GetBytes(rawSalted)
Dim sha1 As New _
System.Security.Cryptography.SHA1CryptoServiceProvider
Dim hashedPwBytes() As Byte = sha1.ComputeHash(saltedPwBytes)
Dim hashedPw As String = Convert.ToBase64String(hashedPwBytes)
' Compare the hashed password with the stored password.
Return hashedPw = storedHashedPW
End Function
Private Function GetHashedPassword(ByVal username As String) As String
' Code that gets the user's hashed password goes here.
' This example uses a hard-coded hashed passcode.
' In general, the hashed passcode should be stored
' outside of the application.
If Trim(username).ToLower = "testuser" Then
Return "ZFFzgfsGjgtmExzWBRmZI5S4w6o="
Else
Return ""
End If
End Function
Private Function GetSalt(ByVal username As String) As String
' Code that gets the user's salt goes here.
' This example uses a hard-coded salt.
' In general, the salt should be stored
' outside of the application.
If Trim(username).ToLower = "testuser" Then
Return "Should be a different random value for each user"
Else
Return ""
End If
End Function
End Class
Создание участника
Далее необходимо реализовать класс, производный от IPrincipal и сделать так, чтобы он возвращал экземпляры класса SampleIIdentity.
Создание класса, реализующего интерфейс IPrincipal
Выберите файл SampleIPrincipal.vb в Обозревателе решений.
Этот класс инкапсулирует удостоверение пользователя. Можно использовать объект My.User для присоединения этого участника к текущему потоку и получения доступа к удостоверению пользователя.
В строке после Public Class SampleIPrincipal добавьте следующий код, задающий наследование от IPrincipal.
Implements System.Security.Principal.IPrincipal
После добавления кода и нажатия на клавишу ВВОД редактор кода создает свойство-заглушку и метод, которые нужно реализовать.
Добавьте закрытое поле для хранения удостоверения, связанного с этим участником.
Private identityValue As SampleIIdentity
Введите следующий код в свойстве Identity.
Return identityValue
Свойство Identity должно возвращать удостоверение пользователя текущего участника.
Введите следующий код в методе IsInRole.
Метод IsInRole определяет, принадлежит ли текущий участник к указанной роли.
Return role = identityValue.Role.ToString
Создайте метод Sub New, инициализирующий новый экземпляр класса SampleIIdentity по имени пользователя и паролю.
Public Sub New(ByVal name As String, ByVal password As String) identityValue = New SampleIIdentity(name, password) End Sub
Этот код устанавливает удостоверение пользователя для класса SampleIPrincipal.
Файл SampleIPrincipal.vb теперь должен содержать следующий код:
Public Class SampleIPrincipal
Implements System.Security.Principal.IPrincipal
Private identityValue As SampleIIdentity
Public ReadOnly Property Identity() As System.Security.Principal.IIdentity Implements System.Security.Principal.IPrincipal.Identity
Get
Return identityValue
End Get
End Property
Public Function IsInRole(ByVal role As String) As Boolean Implements System.Security.Principal.IPrincipal.IsInRole
Return role = identityValue.Role.ToString
End Function
Public Sub New(ByVal name As String, ByVal password As String)
identityValue = New SampleIIdentity(name, password)
End Sub
End Class
Подключение к форме входа
Приложение может использовать форму входа для ввода имени пользователя и пароля. Эту информацию оно затем может использовать для инициализации экземпляра класса SampleIPrincipal и установки удостоверения текущего потока на этот экземпляр с помощью объекта My.User.
Настройка формы входа
Выделите форму LoginForm1 в конструкторе.
Дважды щелкните кнопку ОК, чтобы открыть редактор кода для события Click.
Замените код в методе OK_Click следующим кодом:
Dim samplePrincipal As New SampleIPrincipal( _ Me.UsernameTextBox.Text, Me.PasswordTextBox.Text) Me.PasswordTextBox.Text = "" If (Not samplePrincipal.Identity.IsAuthenticated) Then ' The user is still not validated. MsgBox("The username and password pair is incorrect") Else ' Update the current principal. My.User.CurrentPrincipal = samplePrincipal Me.Close() End If
Тестирование приложения
Теперь, когда приложение содержит код проверки подлинности, можно запустить его и попытаться проверить подлинность пользователя.
Тестирование приложения
Запустите приложение.
Нажмите Проверить подлинность.
Откроется форма входа.
Введите TestUser в поле Имя пользователя и BadPassword в поле Пароль, а затем нажмите кнопку ОК.
Откроется окно с сообщением о том, что данное сочетание имени пользователя и пароля неверно.
Нажмите кнопку ОК, чтобы закрыть окно сообщения.
Нажмите Отмена, чтобы закрыть форму входа.
Теперь надписи в главной форме гласят Пользователь не прошел проверку подлинности и Пользователь не является администратором.
Нажмите Проверить подлинность.
Откроется форма входа.
Введите TestUser в текстовое поле Имя пользователя и Password в текстовое поле Пароль, затем нажмите кнопку ОК. Убедитесь, что пароль вводится в правильном регистре.
Надписи в главной форме теперь гласят Пользователь TestUser прошел проверку подлинности и Пользователь является администратором.
См. также
Задачи
Практическое руководство. Подключение к данным в базе данных
Основные понятия
Ссылки
Другие ресурсы
Проверка подлинности и авторизация в .NET Framework с помощью Visual Basic