2010-08-04 55 views
145

我想下面的代码...密码屏蔽控制台应用程序

string pass = ""; 
Console.Write("Enter your password: "); 
ConsoleKeyInfo key; 

do 
{ 
    key = Console.ReadKey(true); 

    // Backspace Should Not Work 
    if (key.Key != ConsoleKey.Backspace) 
    { 
     pass += key.KeyChar; 
     Console.Write("*"); 
    } 
    else 
    { 
     Console.Write("\b"); 
    } 
} 
// Stops Receving Keys Once Enter is Pressed 
while (key.Key != ConsoleKey.Enter); 

Console.WriteLine(); 
Console.WriteLine("The Password You entered is : " + pass); 

但是这样一来,同时输入密码的回退功能不起作用。 有什么建议吗?

+7

我建议你不要回显任何东西回控制台,因为这会暴露密码的长度。 – 2012-08-15 20:41:45

+7

@RayCheng - 足够公平,但很少有用户界面(除了某些Unix系统上)完全没有回应。为了与其他应用和网站保持一致的用户体验,显示*字符可能是最好的。 – 2014-02-19 14:02:13

+1

@StephenHolt我相当确定每一个我遇到过的基于终端的密码输入都选择不向终端回应。鉴于安全性的好处以及这是Unix世界中众所周知的惯例,我个人认为什么都没有是正确的选择,除非您认为您的用户群可能不熟悉终端的使用(在这种情况下无论如何,最好使用GUI)。 – Ajedi32 2017-01-03 16:00:45

回答

151

Console.Write("\b \b");会从屏幕中删除星号,但你没有你的else块移除先前输入的字符中的任何代码来自您的pass字符串变量。

下面是应该做的,你需要什么相关的代码部分(if..else部分):

// Backspace Should Not Work 
if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter) 
{ 
    pass += key.KeyChar; 
    Console.Write("*"); 
} 
else 
{ 
    if (key.Key == ConsoleKey.Backspace && pass.Length > 0) 
    { 
     pass = pass.Substring(0, (pass.Length - 1)); 
     Console.Write("\b \b"); 
    } 
} 
+1

哦,我以为\ b \ b会带我回两个地方。尽管如此,这似乎正在工作。 – 2010-08-04 10:16:39

+5

@Nadeem:注意退格字符(''\ b'')之间的空格字符('''')。 ''\ b \ b“'把你带回一个地方,然后打印一个空格(它将你带向前一个地方),然后再把你带回来,这样你就会结束被删除的'*'字符。 – dtb 2010-08-04 10:23:38

+10

@Nadeem - 第一个'\ b'将光标移回到一个位置(现在在最后一个'*'字符的下面,'[空格]'字符“打印在星号上,同时又将光标向前移动一个字符,所以最后的'\ b'将光标移回到最后一个'*'过去的位置!(phew - 希望有道理!) – CraigTP 2010-08-04 10:27:32

63

为此,您应该使用System.Security.SecureString

public SecureString GetPassword() 
{ 
    var pwd = new SecureString(); 
    while (true) 
    { 
     ConsoleKeyInfo i = Console.ReadKey(true); 
     if (i.Key == ConsoleKey.Enter) 
     { 
      break; 
     } 
     else if (i.Key == ConsoleKey.Backspace) 
     { 
      if (pwd.Length > 0) 
      { 
       pwd.RemoveAt(pwd.Length - 1); 
       Console.Write("\b \b"); 
      } 
     } 
     else 
     { 
      pwd.AppendChar(i.KeyChar); 
      Console.Write("*"); 
     } 
    } 
    return pwd; 
} 
+0

这只会让我回到两个地方。但我需要的是,当我按Backspace时,最后一个字符应该被删除。就像退格的原始功能一样。 – 2010-08-04 10:08:07

+1

我需要将if(pwd.Length> 0)'嵌套到第一个else语句中以阻止人们删除问题:) – 2012-06-25 10:18:32

+1

完美适用于'System.Net.NetworkCredential()'。 – mbrownnyc 2013-11-07 18:20:21

1

你可以添加你的钥匙到积累链表。

收到退格键时,从列表中删除最后一个键。

当您收到回车键时,将您的列表折叠成一个字符串并完成其余的工作。

+0

声音可以实现,但我将如何从显示屏中删除最后一个字符。 – 2010-08-04 10:09:38

-1

如果我正确地理解了这一点,你试图让退格键同时删除屏幕上的可见*字符和传递变量中的缓存字符?

如果是这样,那么就你else块改成这样:

  else 
      { 
       Console.Write("\b"); 
       pass = pass.Remove(pass.Length -1); 
      } 
+1

这将工作正常,但不会显示退格删除字符。 – 2010-08-04 10:11:45

39

完整的解决方案,香草C#.NET 3.5+

剪切粘贴& :)

using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 

    namespace ConsoleReadPasswords 
    { 
     class Program 
     { 
      static void Main(string[] args) 
      { 
       Console.Write("Password:"); 

       string password = Orb.App.Console.ReadPassword(); 

       Console.WriteLine("Sorry - I just can't keep a secret!"); 
       Console.WriteLine("Your password was:\n<Password>{0}</Password>", password); 

       Console.ReadLine(); 
      } 
     } 
    } 

    namespace Orb.App 
    { 
     /// <summary> 
     /// Adds some nice help to the console. Static extension methods don't exist (probably for a good reason) so the next best thing is congruent naming. 
     /// </summary> 
     static public class Console 
     { 
      /// <summary> 
      /// Like System.Console.ReadLine(), only with a mask. 
      /// </summary> 
      /// <param name="mask">a <c>char</c> representing your choice of console mask</param> 
      /// <returns>the string the user typed in </returns> 
      public static string ReadPassword(char mask) 
      { 
       const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127; 
       int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const 

       var pass = new Stack<char>(); 
       char chr = (char)0; 

       while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER) 
       { 
        if (chr == BACKSP) 
        { 
         if (pass.Count > 0) 
         { 
          System.Console.Write("\b \b"); 
          pass.Pop(); 
         } 
        } 
        else if (chr == CTRLBACKSP) 
        { 
         while (pass.Count > 0) 
         { 
          System.Console.Write("\b \b"); 
          pass.Pop(); 
         } 
        } 
        else if (FILTERED.Count(x => chr == x) > 0) { } 
        else 
        { 
         pass.Push((char)chr); 
         System.Console.Write(mask); 
        } 
       } 

       System.Console.WriteLine(); 

       return new string(pass.Reverse().ToArray()); 
      } 

      /// <summary> 
      /// Like System.Console.ReadLine(), only with a mask. 
      /// </summary> 
      /// <returns>the string the user typed in </returns> 
      public static string ReadPassword() 
      { 
       return Orb.App.Console.ReadPassword('*'); 
      } 
     } 
    } 
+0

感谢您发布完整的解决方案 – user2871239 2014-05-28 12:49:57

+0

一款非常棒的真正即可使用的解决方案。感谢那。 – Lars 2015-05-28 12:15:37

+0

非常方便。谢谢! – pStan 2015-12-14 16:07:25

1

我对退格做了一些修改

 string pass = ""; 
     Console.Write("Enter your password: "); 
     ConsoleKeyInfo key; 

     do 
     { 
      key = Console.ReadKey(true); 

      // Backspace Should Not Work 
      if (key.Key != ConsoleKey.Backspace) 
      { 
       pass += key.KeyChar; 
       Console.Write("*"); 
      } 
      else 
      { 
       pass = pass.Remove(pass.Length - 1); 
       Console.Write("\b \b"); 
      } 
     } 
     // Stops Receving Keys Once Enter is Pressed 
     while (key.Key != ConsoleKey.Enter); 

     Console.WriteLine(); 
     Console.WriteLine("The Password You entered is : " + pass); 
8

矿忽略控制字符和处理换行:

public static string ReadLineMasked(char mask = '*') 
{ 
    var sb = new StringBuilder(); 
    ConsoleKeyInfo keyInfo; 
    while ((keyInfo = Console.ReadKey(true)).Key != ConsoleKey.Enter) 
    { 
     if (!char.IsControl(keyInfo.KeyChar)) 
     { 
      sb.Append(keyInfo.KeyChar); 
      Console.Write(mask); 
     } 
     else if (keyInfo.Key == ConsoleKey.Backspace && sb.Length > 0) 
     { 
      sb.Remove(sb.Length - 1, 1); 

      if (Console.CursorLeft == 0) 
      { 
       Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1); 
       Console.Write(' '); 
       Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1); 
      } 
      else Console.Write("\b \b"); 
     } 
    } 
    Console.WriteLine(); 
    return sb.ToString(); 
} 
+0

工作完美。 – 2016-11-23 09:16:55

+1

我真的不明白为什么标准库中没有这个功能... – Ajedi32 2017-01-03 16:04:08

+0

完美地工作。您可能需要添加代码才能使**'DELETE' _character _ **擦除所有输入的文本。它的关键序列是'CTRL + BACKSPACE',它的char代码是'0x7f'。 – 2017-06-26 16:39:08

0

我已经花了太多时间试图只有输入密码才能发现,我有我的CAPS LOCK后更新罗尼的版本!

有了这个版本,_CapsLockMessage的信息将在输入区域的末尾“浮动”,并以红色显示。

该版本需要更多的代码,并且需要轮询循环。在我的电脑CPU使用率约为3%到4%,但总是可以添加一个小的Sleep()值,以便在需要时减少CPU使用率。

private const string _CapsLockMessage = " CAPS LOCK"; 

    /// <summary> 
    /// Like System.Console.ReadLine(), only with a mask. 
    /// </summary> 
    /// <param name="mask">a <c>char</c> representing your choice of console mask</param> 
    /// <returns>the string the user typed in</returns> 
    public static string ReadLineMasked(char mask = '*') 
    { 
     // Taken from http://stackoverflow.com/a/19770778/486660 
     var consoleLine = new StringBuilder(); 
     ConsoleKeyInfo keyInfo; 
     bool isDone; 
     bool isAlreadyLocked; 
     bool isCapsLockOn; 
     int cursorLeft; 
     int cursorTop; 
     ConsoleColor originalForegroundColor; 

     isDone = false; 
     isAlreadyLocked = Console.CapsLock; 

     while (isDone == false) 
     { 
      isCapsLockOn = Console.CapsLock; 
      if (isCapsLockOn != isAlreadyLocked) 
      { 
       if (isCapsLockOn) 
       { 
        cursorLeft = Console.CursorLeft; 
        cursorTop = Console.CursorTop; 
        originalForegroundColor = Console.ForegroundColor; 
        Console.ForegroundColor = ConsoleColor.Red; 
        Console.Write("{0}", _CapsLockMessage); 
        Console.SetCursorPosition(cursorLeft, cursorTop); 
        Console.ForegroundColor = originalForegroundColor; 
       } 
       else 
       { 
        cursorLeft = Console.CursorLeft; 
        cursorTop = Console.CursorTop; 
        Console.Write("{0}", string.Empty.PadRight(_CapsLockMessage.Length)); 
        Console.SetCursorPosition(cursorLeft, cursorTop); 
       } 
       isAlreadyLocked = isCapsLockOn; 
      } 

      if (Console.KeyAvailable) 
      { 
       keyInfo = Console.ReadKey(intercept: true); 

       if (keyInfo.Key == ConsoleKey.Enter) 
       { 
        isDone = true; 
        continue; 
       } 

       if (!char.IsControl(keyInfo.KeyChar)) 
       { 
        consoleLine.Append(keyInfo.KeyChar); 
        Console.Write(mask); 
       } 
       else if (keyInfo.Key == ConsoleKey.Backspace && consoleLine.Length > 0) 
       { 
        consoleLine.Remove(consoleLine.Length - 1, 1); 

        if (Console.CursorLeft == 0) 
        { 
         Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1); 
         Console.Write(' '); 
         Console.SetCursorPosition(Console.BufferWidth - 1, Console.CursorTop - 1); 
        } 
        else 
        { 
         Console.Write("\b \b"); 
        } 
       } 

       if (isCapsLockOn) 
       { 
        cursorLeft = Console.CursorLeft; 
        cursorTop = Console.CursorTop; 
        originalForegroundColor = Console.ForegroundColor; 
        Console.ForegroundColor = ConsoleColor.Red; 
        Console.Write("{0}", _CapsLockMessage); 
        Console.CursorLeft = cursorLeft; 
        Console.CursorTop = cursorTop; 
        Console.ForegroundColor = originalForegroundColor; 
       } 
      } 
     } 

     Console.WriteLine(); 

     return consoleLine.ToString(); 
    } 
0
 string pass = ""; 
     Console.WriteLine("Enter your password: "); 
     ConsoleKeyInfo key; 

     do 
     { 
      key = Console.ReadKey(true); 

      if (key.Key != ConsoleKey.Backspace) 
      { 
       pass += key.KeyChar; 
       Console.Write("*"); 
      } 
      else 
      { 
       Console.Write("\b \b"); 
       char[] pas = pass.ToCharArray(); 
       string temp = ""; 
       for (int i = 0; i < pass.Length - 1; i++) 
       { 
        temp += pas[i]; 
       } 
       pass = temp; 
      } 
     } 
     // Stops Receving Keys Once Enter is Pressed 
     while (key.Key != ConsoleKey.Enter); 

     Console.WriteLine(); 
     Console.WriteLine("The Password You entered is : " + pass); 
+1

这个答案不会增加任何超出现有答案的内容。另外,好的答案通常应该解释代码,而不是将代码粘贴到答案框中。请阅读[回答] – durron597 2015-08-01 17:53:55

3

我发现了一个bug在shermy的香草C#3.5 .NET解决方案,否则工作的魅力。我也加入了DamianLeszczyński - Vash的SecureString理念,但如果您愿意的话,您可以使用普通的字符串。

错误:如果您在密码提示期间按退格键,且密码的当前长度为0,则会在密码掩码中错误地插入星号。要修复此错误,请修改以下方法。

public static string ReadPassword(char mask) 
    { 
     const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127; 
     int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const 


     SecureString securePass = new SecureString(); 

     char chr = (char)0; 

     while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER) 
     { 
      if (((chr == BACKSP) || (chr == CTRLBACKSP)) 
       && (securePass.Length > 0)) 
      { 
       System.Console.Write("\b \b"); 
       securePass.RemoveAt(securePass.Length - 1); 

      } 
      // Don't append * when length is 0 and backspace is selected 
      else if (((chr == BACKSP) || (chr == CTRLBACKSP)) && (securePass.Length == 0)) 
      { 
      } 

      // Don't append when a filtered char is detected 
      else if (FILTERED.Count(x => chr == x) > 0) 
      { 
      } 

      // Append and write * mask 
      else 
      { 
       securePass.AppendChar(chr); 
       System.Console.Write(mask); 
      } 
     } 

     System.Console.WriteLine(); 
     IntPtr ptr = new IntPtr(); 
     ptr = Marshal.SecureStringToBSTR(securePass); 
     string plainPass = Marshal.PtrToStringBSTR(ptr); 
     Marshal.ZeroFreeBSTR(ptr); 
     return plainPass; 
    } 
4

阅读控制台输入很难,您需要处理特殊的按键,如Ctrl,Alt,光标键和Backspace/Delete。在某些键盘布局上,如Swedish甚至需要使用Ctrl键输入直接存在于美式键盘上的键。我相信试图使用“低级”Console.ReadKey(true)来处理这个问题非常困难,所以最简单和最可靠的方法就是在使用一点WINAPI输入密码期间禁用“控制台输入回显”。

以下示例基于回答Read a password from std::cin问题。

private enum StdHandle 
    { 
     Input = -10, 
     Output = -11, 
     Error = -12, 
    } 

    private enum ConsoleMode 
    { 
     ENABLE_ECHO_INPUT = 4 
    } 

    [DllImport("kernel32.dll", SetLastError = true)] 
    private static extern IntPtr GetStdHandle(StdHandle nStdHandle); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int lpMode); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool SetConsoleMode(IntPtr hConsoleHandle, int dwMode); 

    public static string ReadPassword() 
    { 
     IntPtr stdInputHandle = GetStdHandle(StdHandle.Input); 
     if (stdInputHandle == IntPtr.Zero) 
     { 
      throw new InvalidOperationException("No console input"); 
     } 

     int previousConsoleMode; 
     if (!GetConsoleMode(stdInputHandle , out previousConsoleMode)) 
     { 
      throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not get console mode."); 
     } 

     // disable console input echo 
     if (!SetConsoleMode(stdInputHandle , previousConsoleMode & ~(int)ConsoleMode.ENABLE_ECHO_INPUT)) 
     { 
      throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not disable console input echo."); 
     } 

     // just read the password using standard Console.ReadLine() 
     string password = Console.ReadLine(); 

     // reset console mode to previous 
     if (!SetConsoleMode(stdInputHandle , previousConsoleMode)) 
     { 
      throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not reset console mode."); 
     } 

     return password; 
    } 
6

考虑最多的回答,以及来自它的意见建议,并修改它使用SecureString的,而不是字符串,测试的所有控制键,而不是错误或者写一个额外的“*”屏幕当密码长度为0,我的解决方案是:

public static SecureString getPasswordFromConsole(String displayMessage) { 
    SecureString pass = new SecureString(); 
    Console.Write(displayMessage); 
    ConsoleKeyInfo key; 

    do { 
     key = Console.ReadKey(true); 

     // Backspace Should Not Work 
     if (!char.IsControl(key.KeyChar)) { 
      pass.AppendChar(key.KeyChar); 
      Console.Write("*"); 
     } else { 
      if (key.Key == ConsoleKey.Backspace && pass.Length > 0) { 
       pass.RemoveAt(pass.Length - 1); 
       Console.Write("\b \b"); 
      } 
     } 
    } 
    // Stops Receving Keys Once Enter is Pressed 
    while (key.Key != ConsoleKey.Enter); 
    return pass; 
} 
0

这掩盖了红色正方形的密码,然后恢复到原来的颜色,一旦密码已输入。

它不会阻止用户使用复制/粘贴来获取密码,但如果它更多的是阻止某个人看着你的肩膀,这是一个很好的快速解决方案。

Console.Write("Password "); 
ConsoleColor origBG = Console.BackgroundColor; // Store original values 
ConsoleColor origFG = Console.ForegroundColor; 

Console.BackgroundColor = ConsoleColor.Red; // Set the block colour (could be anything) 
Console.ForegroundColor = ConsoleColor.Red; 

string Password = Console.ReadLine(); // read the password 

Console.BackgroundColor= origBG; // revert back to original 
Console.ForegroundColor= origFG;