ASP.NET标识:生成随机密码

mLa*_*Lar 15 c# asp.net asp.net-mvc

是否有任何内置函数可以创建随机密码?Asp.net简单的memebership曾经有过类似的方法

Dar*_*eal 17

虽然我在派对上有点晚了,但我想分享一下帮助方法,我把它放在一起以ASP.NET Core兼容的方式处理这些场景.

下面的函数确保了一个合适的char分布,在字符串中随机添加所需的字符类型,而不是改变所需的长度(除非边缘情况具有许多所需的唯一字符,这是设计的意思).它还具有对RequiredUniqueChars规则的支持,这是ASP.NET Core Identity框架可用的强度要求之一.

    /// <summary>
    /// Generates a Random Password
    /// respecting the given strength requirements.
    /// </summary>
    /// <param name="opts">A valid PasswordOptions object
    /// containing the password strength requirements.</param>
    /// <returns>A random password</returns>
    public static string GenerateRandomPassword(PasswordOptions opts = null)
    {
        if (opts == null) opts = new PasswordOptions()
        {
            RequiredLength = 8,
            RequiredUniqueChars = 4,
            RequireDigit = true,
            RequireLowercase = true,
            RequireNonAlphanumeric = true,
            RequireUppercase = true
        };

        string[] randomChars = new[] {
            "ABCDEFGHJKLMNOPQRSTUVWXYZ",    // uppercase 
            "abcdefghijkmnopqrstuvwxyz",    // lowercase
            "0123456789",                   // digits
            "!@$?_-"                        // non-alphanumeric
        };

        Random rand = new Random(Environment.TickCount);
        List<char> chars = new List<char>();

        if (opts.RequireUppercase)
            chars.Insert(rand.Next(0, chars.Count), 
                randomChars[0][rand.Next(0, randomChars[0].Length)]);

        if (opts.RequireLowercase)
            chars.Insert(rand.Next(0, chars.Count), 
                randomChars[1][rand.Next(0, randomChars[1].Length)]);

        if (opts.RequireDigit)
            chars.Insert(rand.Next(0, chars.Count), 
                randomChars[2][rand.Next(0, randomChars[2].Length)]);

        if (opts.RequireNonAlphanumeric)
            chars.Insert(rand.Next(0, chars.Count), 
                randomChars[3][rand.Next(0, randomChars[3].Length)]);

        for (int i = chars.Count; i < opts.RequiredLength
            || chars.Distinct().Count() < opts.RequiredUniqueChars; i++)
        {
            string rcs = randomChars[rand.Next(0, randomChars.Length)];
            chars.Insert(rand.Next(0, chars.Count), 
                rcs[rand.Next(0, rcs.Length)]);
        }

        return new string(chars.ToArray());
    }
Run Code Online (Sandbox Code Playgroud)

该函数将一个PasswordOptions对象作为参数,由Microsoft.AspNetCore.Identity程序集提供,但如果您没有安装该程序包,则可以使用两个int /四个bool参数组(或POCO类)轻松替换它.

在您可能的情况下,在ASP.NET Core项目中,您可以 在定义密码要求时ConfigureService使用Startup类的方法中  使用的完全相同的对象:

[...]

// Add ASP.NET Identity support
services.AddIdentity<ApplicationUser, IdentityRole>(
    opts =>
    {
        opts.Password.RequireDigit = true;
        opts.Password.RequireLowercase = true;
        opts.Password.RequireUppercase = true;
        opts.Password.RequireNonAlphanumeric = false;
        opts.Password.RequiredLength = 7;
    })
    .AddEntityFrameworkStores<ApplicationDbContext>();

[...]
Run Code Online (Sandbox Code Playgroud)

有关此辅助函数的其他详细信息,您还可以在我的博客上阅读此文章.

  • @Brian +1,随机在加密上不安全,请使用 System.Security.Cryptography 中的 RandomNumberGenerator 代替 https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator?view=netframework -4.8 (4认同)
  • 谢谢你!fwiw,我修改了一些看起来相似的字符,比如 0 和 O、1 和 l 等。另外,对于那些像我一样想知道的人(假设我没有遗漏一些明显的东西),你可以通过注入来注入 IdentityOptions IOptions&lt;IdentityOptions&gt; 然后访问密码配置,如:identityOptions.Value.Password (2认同)
  • 请不要在密码生成器函数中使用`Random rand = new Random(Environment.TickCount);`。 (2认同)

小智 8

你使用什么框架?

点击这里:

 string password = Membership.GeneratePassword(12, 1);
Run Code Online (Sandbox Code Playgroud)

  • @ 7wp我只是投票,因为谁不知道身份和会员之间的巨大差异,不应该回答使用会员身份的身份问题.这是一个完全没用的答案 (5认同)
  • 如果投票的人可以自己解释,那将是最有帮助的. (2认同)
  • 成员资格不是ASP.NET Identity的一部分,而是它的前身。它实现了一种“更安全”的方法,用于发送带有唯一重置令牌的重置链接。 (2认同)

Dav*_*tte 8

ASP.NET Identity没有生成密码方法.

我不确定您的确切用例,但我认为首选方法是向用户发送重置密码链接,允许用户输入自己的密码.这通常被认为比以纯文本形式发送生成的密码更安全.

请参阅本教程中的"重置密码"部分:http: //www.asp.net/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity


Rub*_*sme 5

我知道这是一个老问题,还有其他人发布了随机生成密码的源代码,但 Membership.GeneratePassword是这样实现的:

幸运的是,这是在 MIT 许可证下获得许可的https://github.com/Microsoft/referencesource/blob/master/LICENSE.txt

public class PasswordStore
{
        private static readonly char[] Punctuations = "!@#$%^&*()_-+=[{]};:>|./?".ToCharArray();
        private static readonly char[] StartingChars = new char[] { '<', '&' };
        /// <summary>Generates a random password of the specified length.</summary>
        /// <returns>A random password of the specified length.</returns>
        /// <param name="length">The number of characters in the generated password. The length must be between 1 and 128 characters. </param>
        /// <param name="numberOfNonAlphanumericCharacters">The minimum number of non-alphanumeric characters (such as @, #, !, %, &amp;, and so on) in the generated password.</param>
        /// <exception cref="T:System.ArgumentException">
        /// <paramref name="length" /> is less than 1 or greater than 128 -or-<paramref name="numberOfNonAlphanumericCharacters" /> is less than 0 or greater than <paramref name="length" />. </exception>
        public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters)
        {
            if (length < 1 || length > 128)
                throw new ArgumentException("password_length_incorrect", nameof(length));
            if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
                throw new ArgumentException("min_required_non_alphanumeric_characters_incorrect", nameof(numberOfNonAlphanumericCharacters));
            string s;
            int matchIndex;
            do
            {
                var data = new byte[length];
                var chArray = new char[length];
                var num1 = 0;
                new RNGCryptoServiceProvider().GetBytes(data);
                for (var index = 0; index < length; ++index)
                {
                    var num2 = (int)data[index] % 87;
                    if (num2 < 10)
                        chArray[index] = (char)(48 + num2);
                    else if (num2 < 36)
                        chArray[index] = (char)(65 + num2 - 10);
                    else if (num2 < 62)
                    {
                        chArray[index] = (char)(97 + num2 - 36);
                    }
                    else
                    {
                        chArray[index] = Punctuations[num2 - 62];
                        ++num1;
                    }
                }
                if (num1 < numberOfNonAlphanumericCharacters)
                {
                    var random = new Random();
                    for (var index1 = 0; index1 < numberOfNonAlphanumericCharacters - num1; ++index1)
                    {
                        int index2;
                        do
                        {
                            index2 = random.Next(0, length);
                        }
                        while (!char.IsLetterOrDigit(chArray[index2]));
                        chArray[index2] = Punctuations[random.Next(0, Punctuations.Length)];
                    }
                }
                s = new string(chArray);
            }
            while (IsDangerousString(s, out matchIndex));
            return s;
        }

        internal static bool IsDangerousString(string s, out int matchIndex)
        {
            //bool inComment = false;
            matchIndex = 0;

            for (var i = 0; ;)
            {

                // Look for the start of one of our patterns 
                var n = s.IndexOfAny(StartingChars, i);

                // If not found, the string is safe
                if (n < 0) return false;

                // If it's the last char, it's safe 
                if (n == s.Length - 1) return false;

                matchIndex = n;

                switch (s[n])
                {
                    case '<':
                        // If the < is followed by a letter or '!', it's unsafe (looks like a tag or HTML comment)
                        if (IsAtoZ(s[n + 1]) || s[n + 1] == '!' || s[n + 1] == '/' || s[n + 1] == '?') return true;
                        break;
                    case '&':
                        // If the & is followed by a #, it's unsafe (e.g. &#83;) 
                        if (s[n + 1] == '#') return true;
                        break;
                }

                // Continue searching
                i = n + 1;
            }
        }

        private static bool IsAtoZ(char c)
        {
            if ((int)c >= 97 && (int)c <= 122)
                return true;
            if ((int)c >= 65)
                return (int)c <= 90;
            return false;
        }
    }
Run Code Online (Sandbox Code Playgroud)


小智 5

Membership.GeneratePassword() 创建不符合身份验证器的密码。

我编写了一个简单的函数,考虑使用 UserManager Validator 创建一个正确的随机密码以分配给用户。

它只是生成随机字符并检查字符是否满足验证器要求。如果不满足要求,它会附加剩余的字符以满足规则。

private string GeneratePassword(MessagePasswordValidator validator)
{
    if (validator == null)
        return null;

    bool requireNonLetterOrDigit = validator.RequireNonLetterOrDigit;
    bool requireDigit = validator.RequireDigit;
    bool requireLowercase = validator.RequireLowercase;
    bool requireUppercase = validator.RequireUppercase;

    string randomPassword = string.Empty;

    int passwordLength = validator.RequiredLength;

    Random random = new Random();
    while (randomPassword.Length != passwordLength)
    {
        int randomNumber = random.Next(48, 122);  // >= 48 && < 122 
        if (randomNumber == 95 || randomNumber == 96) continue;  // != 95, 96 _'

        char c = Convert.ToChar(randomNumber);

        if (requireDigit)
            if (char.IsDigit(c))
                requireDigit = false;

        if (requireLowercase)
            if (char.IsLower(c))
                requireLowercase = false;

        if (requireUppercase)
            if (char.IsUpper(c))
                requireUppercase = false;

        if (requireNonLetterOrDigit)
            if (!char.IsLetterOrDigit(c))
                requireNonLetterOrDigit = false;

        randomPassword += c;
    }

    if (requireDigit)
        randomPassword += Convert.ToChar(random.Next(48, 58));  // 0-9

    if (requireLowercase)
        randomPassword += Convert.ToChar(random.Next(97, 123));  // a-z

    if (requireUppercase)
        randomPassword += Convert.ToChar(random.Next(65, 91));  // A-Z

    if (requireNonLetterOrDigit)
        randomPassword += Convert.ToChar(random.Next(33, 48));  // symbols !"#$%&'()*+,-./

    return randomPassword;
}
Run Code Online (Sandbox Code Playgroud)

并调用:

string password = GeneratePassword(UserManager.PasswordValidator as MessagePasswordValidator);
Run Code Online (Sandbox Code Playgroud)