江西广告网

标题: 在 WinXP 和 Win2003 中使用凭据管理 [打印本页]

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




欢迎光临 江西广告网 (http://bbs.jxadw.com/) Powered by Discuz! X3.2