警路官途全文阅读:实现自定义身份验证和授权-摘自MSDN

来源:百度文库 编辑:九乡新闻网 时间:2024/07/08 15:31:26

实现自定义身份验证和授权-摘自MSDN

      本演练演示如何使用从 IIdentity 和 IPrincipal 派生的类来实现自定义身份验证和授权;如何将 My.User.CurrentPrincipal 设置为从 IPrincipal 派生的类的实例,以便重写应用程序线程的默认标识(即 Windows 标识)。通过 My.User 对象可以立即获得新的用户信息,该对象返回有关线程的当前用户标识的信息。

商务应用程序经常根据用户提供的凭据提供对数据或资源的访问。通常情况下,这种应用程序会检查用户的角色,并根据该角色提供对资源的访问。公共语言运行库根据 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 方法中。

Visual Basic 

复制代码

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 使用 IIdentityIPrincipal 接口作为身份验证和授权的基础。应用程序通过实现这些接口便可以使用自定义用户身份验证,如以下过程所示。

创建实现 IIdentity 的类

1.       在“解决方案资源管理器”中,选择 SampleIIdentity.vb 文件。

此类对用户的标识进行封装。

2.       在 Public Class SampleIIdentity 后面的代码行中,添加以下代码以便从 IIdentity 继承。

Visual Basic 

复制代码

Implements System.Security.Principal.IIdentity

3.       添加相应代码并按 Enter 键之后,代码编辑器将创建您必须实现的存根 (Stub) 属性。

4.       添加私有字段以存储用户名和一个指示是否对该用户进行了身份验证的值。

Visual Basic 

复制代码

Private nameValue As String

Private authenticatedValue As Boolean

Private roleValue As ApplicationServices.BuiltInRole

5.       在 AuthenticationType 属性中输入以下代码。

AuthenticationType 属性需要返回一个指示当前身份验证机制的字符串。

此示例使用显式指定的身份验证,因此该字符串为“Custom Authentication”(自定义身份验证)。如果用户身份验证数据存储在 SQL Server 数据库中,则该值可能是“SqlDatabase”。

Visual Basic 

复制代码

Return "Custom Authentication"

6.       在 IsAuthenticated 属性中输入以下代码。

Visual Basic 

复制代码

Return authenticatedValue

7.       IsAuthenticated 属性需要返回一个值,用于指示是否对用户进行了身份验证。

8.       Name 属性需要返回与此标识关联的用户的名称。

Name 属性中输入以下代码。

Visual Basic 

复制代码

Return nameValue

9.       创建一个返回用户角色的属性。

Visual Basic 

复制代码

Public ReadOnly Property Role() As ApplicationServices.BuiltInRole

    Get

        Return roleValue

    End Get

End Property

10.   创建一个 Sub New 方法,该方法根据名称和密码对用户进行身份验证,然后设置用户名和角色,从而对类进行初始化。

此方法调用一个名为 IsValidNameAndPassword 的方法来确定用户名和密码组合是否有效。

Visual Basic 

复制代码

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

11.   创建一个名为 IsValidNameAndPassword 的方法,该方法确定用户名和密码组合是否有效。

安全注意

身份验证算法必须以安全的方式处理密码。例如,密码不应存储在类字段中。

12.   您不应该将用户密码存储在系统中,因为如果该信息泄露,则不再有安全可言。您可以存储每个用户密码的哈希。(哈希函数可以对数据进行加密编码,从而无法从输出推出输入。)无法从密码的哈希直接确定密码。

13.   但是,恶意用户可能会生成一个所有可能密码的哈希字典,然后查找给定哈希的密码。要避免这类攻击,应在对密码进行哈希处理之前将“Salt”加入密码中,以生成 Salted 哈希。Salt 是每个密码特有的额外数据,可以杜绝预先计算哈希字典的可能。

14.   要防止密码泄露给恶意用户,应仅存储密码的 Salted 哈希,并且最好存储在安全的计算机上。恶意用户要从 Salted 哈希还原密码非常困难。此示例使用 GetHashedPassword 和 GetSalt 方法来加载用户的哈希密码和 Salt。

Visual Basic 

复制代码

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

15.   创建名为 GetHashedPassword 和 GetSalt 的函数,这两个函数返回指定用户的哈希密码和 Salt。

安全注意

应避免在客户端应用程序中对哈希密码和 Salt 进行硬编码,原因有两个:首先,恶意用户也许能够访问它们并找到哈希冲突。其次,您不能更改或撤消用户的密码。应用程序应该从由管理员维护的安全来源获取给定用户的哈希密码和 Salt。

16.   此示例为简便起见使用了硬编码的哈希密码和 Salt,您却应该在产品代码中使用更安全的方法。例如,可以将用户信息存储在 SQL Server 数据库中,然后通过存储过程来访问该信息。有关更多信息,请参见如何:连接到数据库中的数据。

注意

“测试应用程序”一节中提供的密码对应于这个硬编码的哈希密码。

Visual Basic 

复制代码

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 文件现在应包含以下代码:

Visual Basic 

复制代码

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

创建一个 Principal

下一步,您需要实现一个从 IPrincipal 派生的类,并使该类返回 SampleIIdentity 类的实例。

创建一个实现 IPrincipal 的类

1.       在 “解决方案资源管理器”中,选择 SampleIPrincipal.vb 文件。

此类对用户的标识进行封装。使用 My.User 对象,可以将此 Principal 附加到当前线程并访问用户的标识。

2.       在 Public Class SampleIPrincipal 后面的代码行中,添加以下代码以便从 IPrincipal 继承。

Visual Basic 

复制代码

Implements System.Security.Principal.IPrincipal

3.       添加代码并按 Enter 键之后,代码编辑器将创建您必须实现的存根 (Stub) 属性和方法。

4.       添加一个私有字段以存储与此 Principal 关联的标识。

Visual Basic 

复制代码

Private identityValue As SampleIIdentity

5.       在 Identity 属性中输入以下代码。

Visual Basic 

复制代码

Return identityValue

6.       Identity 属性需要返回当前 Principal 的用户标识。

7.       在 IsInRole 方法中输入以下代码。

IsInRole 方法确定当前 Principal 是否属于指定的角色。

Visual Basic 

复制代码

Return role = identityValue.Role.ToString

8.       创建一个 Sub New 方法,该方法使用给定了用户名和密码的 SampleIIdentity 的新实例来初始化类。

Visual Basic 

复制代码

Public Sub New(ByVal name As String, ByVal password As String)

    identityValue = New SampleIIdentity(name, password)

End Sub

9.       此代码设置 SampleIPrincipal 类的用户标识。

SampleIPrincipal.vb 文件现在应包含以下代码:

Visual Basic 

复制代码

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 方法中的代码。

Visual Basic 

复制代码

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 已经过身份验证”和“用户是管理员”。



根据上述原理改写的程序:
Public Class IIdentity
    Implements System.Security.Principal.IIdentity
    Private NameValue As String
    Private AuthenicatedValue As Boolean
    Private RoleValue As ApplicationServices.BuiltInRole
    Private UserData As New WlDataBaseDataSetTableAdapters.Table_UserTableAdapter‘通过程序数据库获得用户数据
    Public ReadOnly Property AuthenticationType() As String Implements System.Security.Principal.IIdentity.AuthenticationType
        Get
            Return "验证通过"
        End Get
    End Property

    Public ReadOnly Property IsAuthenticated() As Boolean Implements System.Security.Principal.IIdentity.IsAuthenticated
        Get
            Return AuthenicatedValue
        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)
        If IsValidNameAndPassword(Name, Password) Then
            NameValue = Name
            AuthenicatedValue = True
            RoleValue = ApplicationServices.BuiltInRole.Administrator
        Else
            NameValue = ""
            AuthenicatedValue = False
            RoleValue = ApplicationServices.BuiltInRole.Guest
        End If
    End Sub

    Public Function IsValidNameAndPassword(ByVal UserName As String, ByVal Password As String) As Boolean
        Dim StoredHashedPW As String = GetHashedPassword(UserName)
        Dim Salt As String = GetSalt(UserName)
        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)
        Return HashedPw = StoredHashedPW
    End Function

    Private Function GetHashedPassword(ByVal UserName As String) As String
        Dim PassDataTable As DataTable = UserData.GetDataByUserName(UserName)
        Dim Pass As String = PassDataTable.Rows(0).Item(2).ToString.Trim
’上两句为通过用户名在程序数据库中获得相关的密码
        If PassDataTable.Rows.Count > 0 Then
            Dim Pass_Salt As String = GetSalt(UserName)
            Dim Pass_RawSalted As String = Pass_Salt & Trim(Pass)
            Dim Pass_SaltedPwBytes() As Byte = System.Text.Encoding.Unicode.GetBytes(Pass_RawSalted)
            Dim Pass_Sha1 As New System.Security.Cryptography.SHA1CryptoServiceProvider
            Dim Pass_HashedPwBytes() As Byte = Pass_Sha1.ComputeHash(Pass_SaltedPwBytes)
            Dim Pass_HashedPw As String = Convert.ToBase64String(Pass_HashedPwBytes)
            Return Pass_HashedPw
        Else
            Return ""
        End If
    End Function

    Private Function GetSalt(ByVal UserName As String) As String
        Dim UserDatatable As DataTable = UserData.GetDataByUserName(UserName)‘判断用户在数据库中是否存在
        If UserDatatable.Rows.Count > 0 Then
            Return "我的应用程序测试"
        Else
            Return ""
        End If
    End Function


End Class