Поделиться через


Пошаговое руководство. Реализация пользовательской проверки подлинности и авторизации

Обновлен: Ноябрь 2007

В этом пошаговом руководстве демонстрируется реализация пользовательской проверки подлинности и авторизации с помощью классов, производных от IIdentity и IPrincipal. Также демонстрируется переопределение заданного по умолчанию удостоверения потока приложения (удостоверения Windows) путем присвоения объекту My.User.CurrentPrincipal значения экземпляра класса, производного от IPrincipal. Информация о новом пользователе доступна немедленно через объект My.User, который возвращает сведения об удостоверении текущего пользователя потока.

Деловые приложения часто обеспечивают доступ к данным или ресурсам на основании учетных данных, предоставляемых пользователем. Обычно такие приложения проверяют роль пользователя и в соответствии с ней предоставляют доступ к ресурсам. Среда CLR обеспечивает поддержку авторизации на основе ролей с использованием учетных записей Windows или пользовательских удостоверений. Дополнительные сведения см. в разделе Безопасность на основе ролей.

Приступая к работе

Прежде всего, создайте проект с главной формой и формой входа в систему и настройте его на использование пользовательской проверки подлинности.

Создание примера приложения

  1. Создайте новый проект Visual Basic, представляющий собой приложение Windows. Дополнительные сведения см. в разделе Практическое руководство. Создание проекта приложения Windows.

    По умолчанию главной форме присваивается имя Form1.

  2. В меню Проект выберите команду Добавить новый элемент.

  3. Выберите шаблон Форма входа и нажмите кнопку Добавить.

    По умолчанию форме входа присваивается имя LoginForm1.

  4. В меню Проект выберите команду Добавить новый элемент.

  5. Выберите шаблон Класс, измените имя на SampleIIdentity и нажмите кнопку Добавить.

  6. В меню Проект выберите команду Добавить новый элемент.

  7. Выберите шаблон Класс, измените имя на SampleIPrincipal и нажмите кнопку Добавить.

  8. В меню Проект выберите команду имя приложения > Свойства.

  9. В конструкторе проекта перейдите на вкладку Приложение.

  10. В раскрывающемся списке Режим проверки подлинности выберите Определяется приложением.

Настройка главной формы

  1. Переключитесь на Form1 в конструкторе форм.

  2. Добавьте элемент Кнопка в форму Form1 из Области элементов.

    Имя кнопки по умолчанию — Button1.

  3. Измените текст кнопки на Проверить подлинность.

  4. Из Панели элементов добавьте элемент управления Надпись в форму Form1.

    Имя надписи по умолчанию — Label1.

  5. Измените текст надписи на пустую строку.

  6. Из Панели элементов добавьте элемент управления Надпись в форму Form1.

    Имя надписи по умолчанию — Label2.

  7. Измените текст надписи на пустую строку.

  8. Дважды щелкните кнопку Button1, чтобы создать обработчик событий Click, а затем откройте редактор кода.

  9. Добавьте следующий код в метод 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

  1. Выберите файл SampleIIdentity.vb в Обозревателе решений.

    Этот класс инкапсулирует удостоверение пользователя.

  2. В строке после Public Class SampleIIdentity добавьте следующий код, задающий наследование от IIdentity.

    Implements System.Security.Principal.IIdentity
    

    После добавления кода и нажатия клавиши ВВОД редактор кода создает свойства-заглушки, которые необходимо реализовать.

  3. Добавьте закрытые поля для хранения имени пользователя и значения, показывающего, выполнена ли проверка подлинности для данного пользователя.

    Private nameValue As String
    Private authenticatedValue As Boolean
    Private roleValue As ApplicationServices.BuiltInRole
    
  4. Введите следующий код в свойстве AuthenticationType.

    Свойство AuthenticationType должно возвращать строку, указывающую выбранный в настоящее время механизм проверки подлинности.

    В данном примере используется явно заданный механизм проверка подлинности, поэтому возвращается строка "Пользовательская проверка подлинности". Если бы данные проверки подлинности пользователя хранились в базе данных SQL Server, строка могла бы иметь значение "SqlDatabase".

    Return "Custom Authentication"
    
  5. Введите следующий код в свойстве IsAuthenticated.

    Return authenticatedValue
    

    Свойство IsAuthenticated должно возвращать значение, указывающее, прошел ли данный пользователь проверку подлинности.

  6. Свойство Name должно возвращать имя пользователя, связанного с данным удостоверением.

    Введите следующий код в свойстве Name.

    Return nameValue
    
  7. Создайте свойство, возвращающее роль пользователя.

    Public ReadOnly Property Role() As ApplicationServices.BuiltInRole
        Get
            Return roleValue
        End Get
    End Property
    
  8. Создайте метод 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
    
  9. Создайте метод с именем IsValidNameAndPassword, определяющий допустимость комбинации имени пользователя и пароля.

    ms172766.alert_security(ru-ru,VS.90).gifПримечание о безопасности.

    Алгоритм проверки подлинности должен обрабатывать пароли безопасным образом. Например, пароль не должен храниться в поле класса.

    Не следует хранить пароли пользователей в системе: утечка этой информации сведет на нет всю безопасность. Можно хранить хэш пароля каждого пользователя. (Хэш-функция шифрует данные так, что входные данные не могут быть получены из выходных данных.) Пароль не может быть определен непосредственно из хэша пароля.

    Однако пользователь-злоумышленник не пожалеть времени и создать словарь хэшей всех возможных паролей, а затем найти пароль по известному хэшу. Для защиты от атак этого типа следует добавить 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
    
  10. Создайте функции с именами GetHashedPassword и GetSalt, возвращающие хэшированный пароль и salt-строку для указанного пользователя.

    ms172766.alert_security(ru-ru,VS.90).gifПримечание о безопасности.

    Следует избегать жесткого кодирования хэшированных паролей и salt-строк в клиентских приложениях по двум причинам. Во-первых, пользователи-злоумышленники смогут получить доступ к ним и найти столкновение хэшей. Во-вторых, нельзя будет изменить или отозвать пароль пользователя. Приложение должно получать хэшированный пароль и salt-строку для данного пользователя из безопасного источника, который обслуживается администратором.

    Хотя для простоты в этом примере используются жестко закодированные хэшированный пароль и salt-строка, в рабочем коде следует использовать более безопасный подход. Например можно хранить информацию о пользователе в базе данных SQL Server и получать доступ к ней с помощью хранимых процедур. Дополнительные сведения см. в разделе Практическое руководство. Подключение к данным в базе данных.

    ms172766.alert_note(ru-ru,VS.90).gifПримечание.

    Пароль, соответствующий этому жестко закодированному хэшированному паролю, дан в разделе "Проверка приложения".

    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

  1. Выберите файл SampleIPrincipal.vb в Обозревателе решений.

    Этот класс инкапсулирует удостоверение пользователя. Можно использовать объект My.User для присоединения этого участника к текущему потоку и получения доступа к удостоверению пользователя.

  2. В строке после Public Class SampleIPrincipal добавьте следующий код, задающий наследование от IPrincipal.

    Implements System.Security.Principal.IPrincipal
    

    После добавления кода и нажатия на клавишу ВВОД редактор кода создает свойство-заглушку и метод, которые нужно реализовать.

  3. Добавьте закрытое поле для хранения удостоверения, связанного с этим участником.

    Private identityValue As SampleIIdentity
    
  4. Введите следующий код в свойстве Identity.

    Return identityValue
    

    Свойство Identity должно возвращать удостоверение пользователя текущего участника.

  5. Введите следующий код в методе IsInRole.

    Метод IsInRole определяет, принадлежит ли текущий участник к указанной роли.

    Return role = identityValue.Role.ToString
    
  6. Создайте метод 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.

Настройка формы входа

  1. Выделите форму LoginForm1 в конструкторе.

  2. Дважды щелкните кнопку ОК, чтобы открыть редактор кода для события Click.

  3. Замените код в методе 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
    

Тестирование приложения

Теперь, когда приложение содержит код проверки подлинности, можно запустить его и попытаться проверить подлинность пользователя.

Тестирование приложения

  1. Запустите приложение.

  2. Нажмите Проверить подлинность.

    Откроется форма входа.

  3. Введите TestUser в поле Имя пользователя и BadPassword в поле Пароль, а затем нажмите кнопку ОК.

    Откроется окно с сообщением о том, что данное сочетание имени пользователя и пароля неверно.

  4. Нажмите кнопку ОК, чтобы закрыть окно сообщения.

  5. Нажмите Отмена, чтобы закрыть форму входа.

    Теперь надписи в главной форме гласят Пользователь не прошел проверку подлинности и Пользователь не является администратором.

  6. Нажмите Проверить подлинность.

    Откроется форма входа.

  7. Введите TestUser в текстовое поле Имя пользователя и Password в текстовое поле Пароль, затем нажмите кнопку ОК. Убедитесь, что пароль вводится в правильном регистре.

    Надписи в главной форме теперь гласят Пользователь TestUser прошел проверку подлинности и Пользователь является администратором.

См. также

Задачи

Практическое руководство. Подключение к данным в базе данных

Основные понятия

Доступ к данным пользователя

Ссылки

Объект My.User

IIdentity

IPrincipal

Другие ресурсы

Проверка подлинности и авторизация в .NET Framework с помощью Visual Basic