我正在创建一个Xaml/C#应用程序,我希望它能弹出一个登录提示。
我想知道是否可以使用CredUIPromptForWindowsCredentials。
我已经看过了Windows安全登录表单?和http://www.pinvoke.net/default.aspx/credui/creduipromptforwindowscredentials.html?diff=y,但是他们没有解释如何处理验证。
我非常想要一个小例子,如果用户输入username = "Bo“和password = "123”,则成功地显示错误消息并允许用户再次尝试。
该应用程序将安装在多台计算机上。
还是根本不可能?
更新
受到这个问题答案的启发,windows /7在C#中显示身份验证对话框
我已经对代码进行了修改,以便按预期的方式工作。
请不要,确认部分只是为了证明概念。
WindowsSecurityDialog.cs
public class WindowsSecurityDialog
{
public string CaptionText { get; set; }
public string MessageText { get; set; }
[DllImport("ole32.dll")]
public static extern void CoTaskMemFree(IntPtr ptr);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct CREDUI_INFO
{
public int cbSize;
public IntPtr hwndParent;
public string pszMessageText;
public string pszCaptionText;
public IntPtr hbmBanner;
}
[DllImport("credui.dll", CharSet = CharSet.Auto)]
private static extern bool CredUnPackAuthenticationBuffer(int dwFlags,
IntPtr pAuthBuffer,
uint cbAuthBuffer,
StringBuilder pszUserName,
ref int pcchMaxUserName,
StringBuilder pszDomainName,
ref int pcchMaxDomainame,
StringBuilder pszPassword,
ref int pcchMaxPassword);
[DllImport("credui.dll", CharSet = CharSet.Auto)]
private static extern int CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere,
int authError,
ref uint authPackage,
IntPtr InAuthBuffer,
uint InAuthBufferSize,
out IntPtr refOutAuthBuffer,
out uint refOutAuthBufferSize,
ref bool fSave,
int flags);
public bool ValidateUser()
{
var credui = new CREDUI_INFO
{
pszCaptionText = CaptionText,
pszMessageText = MessageText
};
credui.cbSize = Marshal.SizeOf(credui);
uint authPackage = 0;
IntPtr outCredBuffer;
uint outCredSize;
bool save = false;
const int loginErrorCode = 1326; //Login Failed
var authError = 0;
while (true)
{
var result = CredUIPromptForWindowsCredentials(ref credui,
authError,
ref authPackage,
IntPtr.Zero,
0,
out outCredBuffer,
out outCredSize,
ref save,
1 /* Generic */);
var usernameBuf = new StringBuilder(100);
var passwordBuf = new StringBuilder(100);
var domainBuf = new StringBuilder(100);
var maxUserName = 100;
var maxDomain = 100;
var maxPassword = 100;
if (result == 0)
{
if (CredUnPackAuthenticationBuffer(0, outCredBuffer, outCredSize, usernameBuf, ref maxUserName,
domainBuf, ref maxDomain, passwordBuf, ref maxPassword))
{
//TODO: ms documentation says we should call this but i can't get it to work
//SecureZeroMem(outCredBuffer, outCredSize);
//clear the memory allocated by CredUIPromptForWindowsCredentials
CoTaskMemFree(outCredBuffer);
var networkCredential = new NetworkCredential()
{
UserName = usernameBuf.ToString(),
Password = passwordBuf.ToString(),
Domain = domainBuf.ToString()
};
//Dummy Code replace with true User Validation
if (networkCredential.UserName == "Bo" && networkCredential.Password == "1234")
return true;
else //login failed show dialog again with login error
{
authError = loginErrorCode;
}
}
}
else return false;
}
}
}App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
var windowsSecurityDialog = new WindowsSecurityDialog
{
CaptionText = "Enter your credentials",
MessageText = "These credentials will be used to connect to YOUR APP NAME";
};
if (windowsSecurityDialog.ValidateUser())
base.OnStartup(e);
}发布于 2012-04-26 14:21:19
您将在WinForms上使用CredUIPromptForWindowsCredentials为WPF和Ookii对话框找到一个完整的实现。
发布于 2012-04-26 07:18:48
当我开始想这可能是可能的时候,我有点害怕。
答案是肯定的和不是的。您可以获得网络域和用户名,但是(谢天谢地),您无法获得实际密码,只有密码的散列。
大量借鉴PInvoke,这里有一个示例WPF应用程序,它引入并输出用户名和密码。
码
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Interop;
namespace LoginDialog
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Declare/initialize variables.
bool save = false;
int errorcode = 0;
uint dialogReturn;
uint authPackage = 0;
IntPtr outCredBuffer;
uint outCredSize;
// Create the CREDUI_INFO struct.
CREDUI_INFO credui = new CREDUI_INFO();
credui.cbSize = Marshal.SizeOf(credui);
credui.pszCaptionText = "Connect to your application";
credui.pszMessageText = "Enter your credentials!";
credui.hwndParent = new WindowInteropHelper(this).Handle;
// Show the dialog.
dialogReturn = CredUIPromptForWindowsCredentials(
ref credui,
errorcode,
ref authPackage,
(IntPtr)0, // You can force that a specific username is shown in the dialog. Create it with 'CredPackAuthenticationBuffer()'. Then, the buffer goes here...
0, // ...and the size goes here. You also have to add CREDUIWIN_IN_CRED_ONLY to the flags (last argument).
out outCredBuffer,
out outCredSize,
ref save,
0); // Use the PromptForWindowsCredentialsFlags Enum here. You can use multiple flags if you seperate them with | .
if (dialogReturn == 1223) // Result of 1223 means the user canceled. Not sure if other errors are ever returned.
textBox1.Text += ("User cancelled!");
if (dialogReturn != 0) // Result of something other than 0 means...something, I'm sure. Either way, failed or canceled.
return;
var domain = new StringBuilder(100);
var username = new StringBuilder(100);
var password = new StringBuilder(100);
int maxLength = 100; // Note that you can have different max lengths for each variable if you want.
// Unpack the info from the buffer.
CredUnPackAuthenticationBuffer(0, outCredBuffer, outCredSize, username, ref maxLength, domain, ref maxLength, password, ref maxLength);
// Clear the memory allocated by CredUIPromptForWindowsCredentials.
CoTaskMemFree(outCredBuffer);
// Output info, escaping whitespace characters for the password.
textBox1.Text += String.Format("Domain: {0}\n", domain);
textBox1.Text += String.Format("Username: {0}\n", username);
textBox1.Text += String.Format("Password (hashed): {0}\n", EscapeString(password.ToString()));
}
public static string EscapeString(string s)
{
// Formatted like this only for you, SO.
return s
.Replace("\a", "\\a")
.Replace("\b", "\\b")
.Replace("\f", "\\f")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t")
.Replace("\v", "\\v");
}
#region DLLImports
[DllImport("ole32.dll")]
public static extern void CoTaskMemFree(IntPtr ptr);
[DllImport("credui.dll", CharSet = CharSet.Unicode)]
private static extern uint CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere, int authError, ref uint authPackage, IntPtr InAuthBuffer,
uint InAuthBufferSize, out IntPtr refOutAuthBuffer, out uint refOutAuthBufferSize, ref bool fSave, PromptForWindowsCredentialsFlags flags);
[DllImport("credui.dll", CharSet = CharSet.Unicode)]
private static extern bool CredUnPackAuthenticationBuffer(int dwFlags, IntPtr pAuthBuffer, uint cbAuthBuffer, StringBuilder pszUserName, ref int pcchMaxUserName, StringBuilder pszDomainName, ref int pcchMaxDomainame, StringBuilder pszPassword, ref int pcchMaxPassword);
#endregion
#region Structs and Enums
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct CREDUI_INFO
{
public int cbSize;
public IntPtr hwndParent;
public string pszMessageText;
public string pszCaptionText;
public IntPtr hbmBanner;
}
private enum PromptForWindowsCredentialsFlags
{
/// <summary>
/// The caller is requesting that the credential provider return the user name and password in plain text.
/// This value cannot be combined with SECURE_PROMPT.
/// </summary>
CREDUIWIN_GENERIC = 0x1,
/// <summary>
/// The Save check box is displayed in the dialog box.
/// </summary>
CREDUIWIN_CHECKBOX = 0x2,
/// <summary>
/// Only credential providers that support the authentication package specified by the authPackage parameter should be enumerated.
/// This value cannot be combined with CREDUIWIN_IN_CRED_ONLY.
/// </summary>
CREDUIWIN_AUTHPACKAGE_ONLY = 0x10,
/// <summary>
/// Only the credentials specified by the InAuthBuffer parameter for the authentication package specified by the authPackage parameter should be enumerated.
/// If this flag is set, and the InAuthBuffer parameter is NULL, the function fails.
/// This value cannot be combined with CREDUIWIN_AUTHPACKAGE_ONLY.
/// </summary>
CREDUIWIN_IN_CRED_ONLY = 0x20,
/// <summary>
/// Credential providers should enumerate only administrators. This value is intended for User Account Control (UAC) purposes only. We recommend that external callers not set this flag.
/// </summary>
CREDUIWIN_ENUMERATE_ADMINS = 0x100,
/// <summary>
/// Only the incoming credentials for the authentication package specified by the authPackage parameter should be enumerated.
/// </summary>
CREDUIWIN_ENUMERATE_CURRENT_USER = 0x200,
/// <summary>
/// The credential dialog box should be displayed on the secure desktop. This value cannot be combined with CREDUIWIN_GENERIC.
/// Windows Vista: This value is not supported until Windows Vista with SP1.
/// </summary>
CREDUIWIN_SECURE_PROMPT = 0x1000,
/// <summary>
/// The credential provider should align the credential BLOB pointed to by the refOutAuthBuffer parameter to a 32-bit boundary, even if the provider is running on a 64-bit system.
/// </summary>
CREDUIWIN_PACK_32_WOW = 0x10000000,
}
#endregion
}
}测试
LoginDialog的新WPF应用程序。TextBox放到名为textBox1的MainWindow.xaml文件中。MainWindow.xaml.cs文件中的代码。样本输出
给定密码“密码”,这是输出。
Domain:
Username: EXAMPLE\fake
Password (hashed): @@D\a\b\f\n\rgAAAAAU-JPAAAAAAweFpM4nPlOUfKi83JLsl4jjh6nMX34yiH注释
这适用于WPF。它可以与权利权限一起使用Silverlight。
我不知道为什么会有人为了合法的定制验证而这么做。如果您想为您的应用程序创建一个登录名,我建议客户端通过SSL (https://)到ASP.NET页面或web服务来检查使用LINQ提供的凭据)进行连接。然后,它可以向客户端发送一个pass/fail响应。
哦,为了上帝的爱和所有神圣的一切,盐分和散列用户密码。
注意:如果你想使用这个登录来阻止用户在没有账户/付费的情况下使用你的应用程序,那么所有这些都不足以阻止人们进行逆向工程和破解应用程序(例如,欺骗它以为它收到了密码信息)。这种DRM是另一种游戏。
https://stackoverflow.com/questions/10242067
复制相似问题