2012-04-20 37 views
7

Tôi đang tạo ứng dụng Xaml/C# và tôi muốn ứng dụng bật lên bằng Nhắc đăng nhập.Bảo mật Windows Xác thực đăng nhập tùy chỉnh

Tôi muốn biết nếu có thể sử dụng CredUIPromptForWindowsCredentials.

  • Hiện Bảo mật * Windows Dialog
  • Lấy tên vào & mật khẩu
  • Thực hiện Tuỳ chỉnh xác nhận
  • Nếu succes xác nhận -> tiếp tục ứng dụng
  • else if xác nhận thất bại -> -inform sử dụng của tên người dùng không hợp lệ hoặc mật khẩu

Tôi đã xem Windows Security login form?http://www.pinvoke.net/default.aspx/credui/creduipromptforwindowscredentials.html?diff=y nhưng chúng không giải thích cách xử lý xác thực.

Tôi thực sự thích một ví dụ nhỏ, nếu người dùng nhập tên người dùng = "Bo" và mật khẩu = "123" thì thông báo lỗi hiển thị thành công khác và cho phép người dùng thử lại.

Ứng dụng sẽ được cài đặt trên nhiều máy tính.

Hoặc điều này đơn giản là không thể?

Cập nhật

Lấy cảm hứng từ câu trả lời trong câu hỏi này Show Authentication dialog in C# for windows Vista/7

tôi đã sửa đổi mã để làm việc như mong đợi.

Vui lòng không, rằng phần xác thực chỉ dành cho bằng chứng về khái niệm.

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); 
     } 
+0

Tôi nghĩ tốt hơn là tạo biểu mẫu tùy chỉnh cho việc này. Dễ quản lý và ít phức tạp hơn. –

+1

Tôi thực sự đã có một hình thức tùy chỉnh, chỉ muốn xem nếu nó có thể sử dụng cửa sổ riêng. Ngoài ra tôi không phải là tìm kiếm tốt nhất :-) – gulbaek

+0

Và bởi "không phải là tìm kiếm tốt nhất", bạn có nghĩa là nó không giống với hộp thoại bảo mật cửa sổ đủ để đánh lừa người dùng. – SPE

Trả lời

4

Bạn sẽ tìm thấy một triển khai hoàn chỉnh cho WPF và WinForms sử dụng CredUIPromptForWindowsCredentials tại Ookii dialogs.

3

tôi là một chút sợ hãi khi tôi bắt đầu suy nghĩ điều này có thể thực hiện được.

Câu trả lời là có và không. Bạn có thể giữ tên miền và tên người dùng mạng, nhưng (cảm ơn lòng tốt), bạn không thể giữ mật khẩu thực, chỉ có một mật khẩu của mật khẩu.

Vay nặng từ PInvoke, đây là một mẫu ứng dụng WPF mang đến và xuất ra tên người dùng và mật khẩu.

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 
    } 
} 

thử nghiệm

  1. Tạo một ứng dụng WPF mới gọi là LoginDialog.
  2. Thả một số TextBox vào tệp MainWindow.xaml được cung cấp có tên textBox1.
  3. Thay thế mã trong tệp MainWindow.xaml.cs.
  4. Chạy!

Sample Output

Với mật khẩu "password", đây là sản phẩm.

Domain: 
Username: EXAMPLE\fake 
Password (hashed): @@D\a\b\f\n\rgAAAAAU-JPAAAAAAweFpM4nPlOUfKi83JLsl4jjh6nMX34yiH 

Comments

này làm việc cho WPF. Nó có thể làm việc cho Silverlight với right permissions.

Tôi không biết tại sao mọi người sẽ làm điều này để xác thực tùy chỉnh hợp pháp. Nếu bạn muốn tạo đăng nhập cho ứng dụng của mình, tôi khuyên bạn nên kết nối khách hàng qua SSL (https: //) với trang ASP.NET hoặc dịch vụ web sẽ kiểm tra thông tin đăng nhập được cung cấp bằng LINQ to SQL. Sau đó nó có thể gửi cho khách hàng một đáp ứng pass/fail.

Ồ, và vì tình yêu của Thiên Chúa và tất cả những điều đó là thánh thiện, salt and hash your users' passwords.

Lưu ý: Nếu bạn muốn sử dụng thông tin đăng nhập này để ngăn người dùng sử dụng ứng dụng của bạn mà không cần có tài khoản/thanh toán, tất cả các chữ viết trên, nhưng sẽ không đủ để ngăn mọi người khỏi kỹ thuật đảo ngược và bẻ khóa ứng dụng (ví dụ, lừa nó vào suy nghĩ rằng nó đã nhận được thông điệp vượt qua). Đó là loại DRM là một toàn bộ 'ballball nother.

+0

Đã cập nhật câu hỏi của tôi bằng một giải pháp khả thi. cảm ơn bạn đã chia sẻ thông tin về bảo mật, nhưng câu hỏi này là nhiều hơn về việc sử dụng biểu mẫu hộp thoại bảo mật của cửa sổ. Tôi đã sử dụng xác thực người dùng an toàn trong số các biện pháp phòng ngừa khác. – gulbaek

+1

Bạn có thể lấy mật khẩu chưa băm bằng cách giải mã nó trong khi giải nén 'CredUnPackAuthenticationBuffer (1, ...' – HodlDwon

Các vấn đề liên quan