简介 有时,应用程序要求有用户提供的凭据以便访问受保护的资源,如数据库或 FTP 网站。然而,获取并用户的 ID 和密码可能会给您的系统带来风险。如有可能,您根本不应该让用户提供凭据(例如,通过对数据库使用集成身份验证),但有时这无法避免。如果您确实要求用户提供凭据,并且您的应用程序将运行在 Microsoft??XP 或 Microsoft? Server 2003 上,则相应的操作系统会提供函数以使此任务变得容易一些。
的用户名和密码 Windows XP 和 Windows Server 2003 使用一种称为“存储的用户名和密码”的功能(参见图 1)将一组凭据与单个 Windows 用户帐户相关联,并使用数据保护 API (DPAPI) 来存储这些凭据。
图 1. Windows XP 中的 Credential Management 对话框 如果您的应用程序运行在 Windows XP 或 Windows .NET 上,则可以使用凭据管理 API 函数来提示用户输入凭据。这些 API 可以为您提供一致的用户界面(参见图 2),并使您可以自动支持由操作系统来缓存这些凭据。
图 2. 标准 Windows XP 凭据对话框 有关在应用程序中请求、存储和使用用户凭据所涉及问题的详细讨论,请参阅 Michael Howard 与 David LeBlanc 合著的 Writing Secure Code 一书。建议您阅读该书,以获取更多信息。在本文中,我将只是向您说明如何在 Microsoft?Visual Basic?.NET 和 应用程序中使用凭据管理 API。
在 .NET 中创建凭据 API 类 声明 API 函数 因为这些凭据管理函数是 Win32 API 调用,您将需要创建 extern () 或 Declare (Visual Basic .NET) 定义以便访问它们。除了这些函数本身以外,还需要使用一些常数和结构来访问它们。这些常数被组织为预定义的常数组,所以我已经选择在 .NET 代码中以枚举形式实现这些常数组,以使 API 调用更加易于使用。
Private Declare Unicode _
Function CredUIPromptForCredentials _
Lib "credui" Alias "CredUIPromptForCredentialsW" _
(ByRef creditUR As CREDUI_INFO, _
ByVal targetName As String, _
ByVal reserved1 As IntPtr, _
ByVal iError As Integer, _
ByVal userName As StringBuilder, _
ByVal maxUserName As Integer, _
ByVal password As StringBuilder, _
ByVal maxPassword As Integer, _
ByRef iSave As Integer, _
ByVal flags As CREDUI_FLAGS) _
As CredUIReturnCodes
Private Declare Unicode _
Function CredUIParseUserName _
Lib "credui" Alias "CredUIParseUserNameW" _
(ByVal userName As String, _
ByVal user As StringBuilder, _
ByVal userMaxChars As Integer, _
ByVal domain As StringBuilder, _
ByVal domainMaxChars As Integer) _
As CredUIReturnCodes
Private Declare Unicode _
Function CredUIConfirmCredentials _
Lib "credui" Alias "CredUIConfirmCredentialsW" _
(ByVal targetName As String, _
ByVal confirm As Boolean) _
As CredUIReturnCodes
Public Declare Auto _
Function DeleteObject Lib "Gdi32" _
(ByVal hObject As IntPtr) As Boolean
注:我在代码中包括了 GDI32 库中的 DeleteObject API 调用,因为如果您决定将自己的位图传递给 CredUIPromptForCredentials API,则您将需要使用该调用。当我在下文中演示自定义位图的使用时,您将可以了解该 API 的使用方法。
常数和结构声明 对于许多 Win32 API 调用,您都需要一组支持常数(在本文中,我选择将其表示为枚举),并且还可能需要一个或两个结构声明。凭据 API 也遵循上述一般性规则,并且需要多种常数和一个结构。在我的 .NET 类中,我添加了一个枚举来表示 CredUIPromptForCredentials 的标志参数,添加了另外一个枚举来表示全部三个凭据 API 调用可能产生的返回代码集,并添加了一个 CREDUI_INFO 结构声明。
Public Enum CREDUI_FLAGS
INCORRECT_PASSWORD = &H1
DO_NOT_PERSIST = &H2
REQUEST_ADMINISTRATOR = &H4
EXCLUDE_CERTIFICATES = &H8
REQUIRE_CERTIFICATE = &H10
SHOW_SAVE_CHECK_BOX = &H40
ALWAYS_SHOW_UI = &H80
REQUIRE_SMARTCARD = &H100
PASSWORD_ONLY_OK = &H200
VALIDATE_USERNAME = &H400
COMPLETE_USERNAME = &H800
PERSIST = &H1000
SERVER_CREDENTIAL = &H4000
EXPECT_CONFIRMATION = &H20000
GENERIC_CREDENTIALS = &H40000
USERNAME_TARGET_CREDENTIALS = &H80000
KEEP_USERNAME = &H100000
End Enum
Public Enum CredUIReturnCodes As Integer
NO_ERROR = 0
ERROR_CANCELLED = 1223
ERROR_NO_SUCH_LOGON_SESSION = 1312
ERROR_NOT_FOUND = 1168
ERROR_INVALID_ACCOUNT_NAME = 1315
ERROR_INSUFFICIENT_BUFFER = 122
ERROR_INVALID_PARAMETER = 87
ERROR_INVALID_FLAGS = 1004
End Enum
Public Structure CREDUI_INFO
Public cbSize As Integer
Public hwndParent As IntPtr
Public pszMessageText As String
Public pszCaptionText As String
Public hbmBanner As IntPtr
End Structure
为 API 调用创建包装函数
此步骤不是必需的。您可以简单地将 API 声明为 Public(而不是像我的代码一样,使其声明为 Private),并从应用程序中直接调用它们。不过,我发现调用 API 经常需要完成一些工作,因而我喜欢通过包装 API 调用来向代码的最终用户隐藏这些调用细节。
Private Const MAX_USER_NAME As Integer = 100
Private Const MAX_PASSWORD As Integer = 100
Private Const MAX_DOMAIN As Integer = 100
Public Shared Function PromptForCredentials( _
ByRef creditUI As CREDUI_INFO, _
ByVal targetName As String, _
ByVal netError As Integer, _
ByRef userName As String, _
ByRef password As String, _
ByRef save As Boolean, _
ByVal flags As CREDUI_FLAGS) _
As CredUIReturnCodes
Dim saveCredentials As Integer
Dim user As New StringBuilder(MAX_USER_NAME)
Dim pwd As New StringBuilder(MAX_PASSWORD)
saveCredentials = Convert.ToInt32(save)
creditUI.cbSize = Marshal.SizeOf(creditUI)
Dim result As CredUIReturnCodes
result = CredUIPromptForCredentials( _
creditUI, targetName, _
IntPtr.Zero, netError, _
user, MAX_USER_NAME, _
pwd, MAX_PASSWORD, _
saveCredentials, flags)
save = Convert.ToBoolean(saveCredentials)
userName = user.ToString
password = pwd.ToString
Return result
End Function
Public Shared Function ParseUserName(ByVal userName As String, _
ByRef userPart As String, _
ByRef domainPart As String) _
As CredUIReturnCodes
Dim user As New StringBuilder(MAX_USER_NAME)
Dim domain As New StringBuilder(MAX_DOMAIN)
Dim result As CredUIReturnCodes
result = CredUIParseUserName(userName, _
user, MAX_USER_NAME, _
domain, MAX_DOMAIN)
userPart = user.ToString()
domainPart = domain.ToString()
Return result
End Function
Public Shared Function ConfirmCredentials(ByVal target As String, _
ByVal confirm As Boolean) As CredUIReturnCodes
Return CredUIConfirmCredentials(target, confirm)
End Function
注:为了便于使用,我已经使我的所有函数都成为 Shared/Static 函数。因为它们没有将任
【责编:admin】
--------------------next---------------------
阅读(1343) | 评论(0) | 转发(0) |