Share via


逐步解說:實作自訂驗證和授權

更新:2007 年 11 月

本逐步解說會示範如何利用衍生自 IIdentityIPrincipal 的類別,實作自訂驗證和授權。本逐步解說也會示範如何將 My.User.CurrentPrincipal 設定為衍生自 IPrincipal 之類別的執行個體,以覆寫應用程式執行緒的預設識別 (Identity),即 Windows 識別。您可以透過 My.User 物件立即獲得新使用者資訊,該物件會傳回執行緒之目前使用者身分的相關資訊。

商務應用程式通常根據使用者所提供的認證,提供對資料或資源的存取權。基本上,這類應用程式會檢查使用者的角色並根據該角色提供存取的資源。Common Language Runtime 會根據 Windows 帳戶或自訂識別,提供角色架構授權的支援。如需詳細資訊,請參閱以角色為基礎的安全性

使用者入門

首先,使用主要表單和登入表單設定專案,然後設定專案以使用自訂驗證。

若要建立範例應用程式

  1. 建立新的 Visual Basic Windows 應用程式專案。如需詳細資訊,請參閱 HOW TO:建立 Windows 應用程式專案

    主要表單的預設名稱為 Form1。

  2. 在 [專案] 功能表上,按一下 [加入新項目]。

  3. 選取 [登入表單] 範本,然後按一下 [加入]。

    登入表單的預設名稱為 LoginForm1。

  4. 在 [專案] 功能表上,按一下 [加入新項目]。

  5. 選取 [類別] 範本,將名稱變更為 SampleIIdentity,然後按一下 [加入]。

  6. 在 [專案] 功能表上,按一下 [加入新項目]。

  7. 選取 [類別] 範本,將名稱變更為 SampleIPrincipal,然後按一下 [加入]。

  8. 在 [專案] 功能表上,按一下 [<ApplicationName> 屬性]。

  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 會使用 IIdentityIPrincipal 介面做為驗證和授權的基準。實作這些介面後,您的應用程式就可以使用自訂使用者驗證,如這些程序所示範。

若要建立實作 IIdentity 的類別

  1. 在 [方案總管] 中選取 [SampleIIdentity.vb] 檔案。

    此類別會封裝使用者的識別。

  2. 在 Public Class SampleIIdentity 的下一行中,加入下列程式碼,以繼承 IIdentity

    Implements System.Security.Principal.IIdentity
    

    加入該程式碼並按 ENTER 鍵後,程式碼編輯器會建立您必須實作的 Stub 屬性。

  3. 加入私用欄位以存放使用者名稱,以及指示是否已驗證使用者的值。

    Private nameValue As String
    Private authenticatedValue As Boolean
    Private roleValue As ApplicationServices.BuiltInRole
    
  4. AuthenticationType 屬性中輸入下列程式碼。

    AuthenticationType 屬性必須傳回會指出目前驗證機制的字串。

    這個範例會使用明確指定的驗證,所以該字串會是 "Custom Authentication"。如果使用者驗證資料存放在 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 的方法,以判斷使用者名稱和密碼組合是否有效。

    安全性注意事項:

    驗證演算法必須安全地處理密碼。例如,密碼不應該存放在類別欄位中。

    您不應該將使用者密碼存放在系統中,因為如果遺漏了該項資訊,就毫無安全性可言。您可以存放每個使用者密碼的「雜湊」(雜湊函數會將資料混合,所以無法從輸出中推論輸入),因此無法直接從密碼的雜湊中判斷密碼。

    不過,惡意使用者可以花時間產生所有可能之密碼的雜湊字典,然後針對指定的雜湊查詢密碼。若要防止此類型的攻擊,您應該在雜湊之前將「Salt」加入至密碼中,以產生 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。

    安全性注意事項:

    您應該避免將雜湊密碼和 Salt 硬式編碼至用戶端應用程式中,其原因有兩個。第一,惡意使用者可加以存取,並找到雜湊衝突。第二,您無法變更或撤銷使用者的密碼。應用程式應該針對指定的使用者,從系統管理員所維護的安全來源中取得雜湊密碼和 Salt。

    雖然為了容易了解之故,此範例會具有硬式編碼的雜湊密碼和 Salt,但您應該在實際執行程式碼中使用更安全的處理方法。例如,您可以將使用者資訊存放在 SQL Server 資料庫中,然後使用預存程序 (Stored Procedure) 加以存取。如需詳細資訊,請參閱 HOW TO:連接至資料庫中的資料

    注意事項:

    「測試應用程式」一節中會提供與此硬式編碼之雜湊密碼對應的密碼。

    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
    

    加入該程式碼並按 ENTER 鍵後,程式碼編輯器會建立您必須實作的 Stub 屬性和方法。

  3. 加入私用欄位,以存放與此主體關聯的識別。

    Private identityValue As SampleIIdentity
    
  4. Identity 屬性中輸入下列程式碼。

    Return identityValue
    

    Identity 屬性必須傳回目前主體的使用者身分。

  5. IsInRole 方法中輸入下列程式碼。

    IsInRole 方法會判斷目前主體是否屬於指定的角色。

    Return role = identityValue.Role.ToString
    
  6. 針對指定的使用者和密碼,利用 SampleIIdentity 的新執行個體建立會將類別初始化的 Sub New 方法。

    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 已驗證] 和 [使用者是系統管理員]。

請參閱

工作

HOW TO:連接至資料庫中的資料

概念

存取使用者資料

參考

My.User 物件

IIdentity

IPrincipal

其他資源

.NET Framework 中使用 Visual Basic 進行的驗證和授權