密码不应包含用户的帐户名或用户全名中超过两个连续字符的部分

本文关键字:用户 两个 连续 字符 全名 包含 密码 中超 | 更新日期: 2023-09-27 18:32:33

实际上,我有一个小问题是我正在开发具有注册用户表单和验证端密码的应用程序不得包含用户名,并且不得包含连续的 2 个字母表单用户名。

假设用户名 ID 为"Aspnetmvc"那么密码不应该包含整个单词"Aspnetmvc",甚至不应该包含用户名的一部分,如asp,net,mvc。这可以通过自定义逻辑来解决,但我想做的是通过编程逻辑来解决,但我想做的是找到用正则表达式解决这个问题的方法。

有人知道这个 C# 吗?

密码不应包含用户的帐户名或用户全名中超过两个连续字符的部分

一个非常简单的解决方案是创建一个方法,该方法将从用户名和真实姓名中提取所有可能的 3 个字母组合,并检查这些是否是密码的一部分。3个字符(超过2个字符(的每个可能部分的方法可以写成一个简单的扩展方法,然后你可以使用IEnumerable.Any方法来查看这些部分中的任何一个是否是密码的一部分:

using System;
using System.Linq;
using System.Collections.Generic;
namespace ConsoleApplication5
{
    static class Program
    {
        static void Main(string[] args)
        {
            string password = "1234567890";
            string username = "125689";
            string realName = "890";
            bool usernameOk = !username.AllPartsOfLength(3)
                .Any(part => password.Contains(part));
            bool realNameOk = !realName.AllPartsOfLength(3)
                .Any(part => password.Contains(part));
        }
        public static IEnumerable<string> AllPartsOfLength(this string value, int length)
        {
            for (int startPos = 0; startPos <= value.Length - length; startPos++)
            {
                yield return value.Substring(startPos, length);
            }
            yield break;
        }
    }
}

我发现这比任何包含正则表达式的解决方案都更容易阅读。

您甚至可以执行以下操作:

passwordOk = !username.AllPartsofLength(3)
    .Concat(realName.AllPartsOfLength(3))
    .Any(part => password.Contains(part));

由于这些使用惰性求值,因此在找到第一部分时,求值将停止。

确实没有必要,也没有充分的理由尝试使用正则表达式来执行此操作。唯一可以使用的表达式是检查字符串中是否存在任何 3 个字母部分的表达式。因此,您仍然需要将字符串拆分为 3 个部分,然后构建一个表达式,让运行时为此构建一个状态机,根据输入检查它,然后丢弃表达式。对于手头的问题来说,这是昂贵的方式。

它看起来像这样:

IEnumerable<string> parts = username.AllPartsOfLength(3)
    .Concat(realName.AllPartsOfLength(3))
    .Select(part => Regex.Escape(part));
string regex = "(" + string.Join("|", parts) + ")";
bool isPasswordOk = !Regex.Match(regex).Success;

添加了基准

根据 sln 的要求,一个简短的基准测试:

方法:仅字符串操作 所用时间:26,0015 毫秒。通过:3333。6666 失败。

方法: 正则表达式字符串连接所有部件 所用时间:486,0278毫秒。通过:3333。6666 失败。

方法:RegexZeroWidthPlusOneAndDotSplat 所用时间:5686,3252毫秒。通过:3333。6666 失败。

方法:正则表达式零宽度 所用时间:2659,1521毫秒。通过:3333。6666 失败。

编辑做了另一个测试,删除了 e.*,但额外的.留在那里

方法:正则表达式零宽度加一 所用时间:2601,1488毫秒。通过:3333。6666 失败。

如您所见,.*导致另一个 50% 的延迟,并且所有使用正则表达式拆分字符串的解决方案都比使用字符串慢得多。加入以创建一个大表达式。到目前为止,明显的赢家没有使用正则表达式。

.*constantconstant慢这一事实的解释可能是由于.*将首先抓取整个输入,然后开始回溯(从字符串的末尾(以查找常量,而constant将只查找constant的第一次出现。

一个简单的测试似乎证实了这一点(使用 .*? 而不是 .*(:

方法:RegexZeroWidthPlusOneDotSplat不情愿 所用时间:2646,1514毫秒。通过:3333。6666 失败。

确实对代码进行了一些更改,删除了区分大小写检查(OP 未要求(我删除了参数验证,我将代码更改为提前失败。这确保了不同方法之间的公平比较。代码可以在这里找到。

你应该让正则表达式为你做这项工作(?=(..)).

重做 4-29

static class Program
{
    static void Main(string[] args)
    {
        string Password = "(O*@aJY^+{PC";
        string Account  = "email@Abc.com";
        string Name     = "Ted Nelson";
        if (Password.IsNotSequentialChars(Account, 2) && Password.IsNotSequentialChars(Name, 2))
            Console.WriteLine("Passed");
        else
            Console.WriteLine("Failed");
    }
    public static bool IsNotSequentialChars(this string Src, string Dest, int check_len)
    {
        if (check_len < 1 || Src.Length < check_len) return true;
        Match m = Regex.Match(Src, "(?=(.{" + check_len + "})).");
        bool bOK = m.Success;
        while (bOK && m.Success)
        {
            // Edit: remove unnecessary '.*' from regex.
            // And btw, is regex needed at all in this case?
            bOK = !Regex.Match(Dest, "(?i)" + Regex.Escape(m.Groups[1].Value)).Success;
            if (!bOK)
                Console.WriteLine("Destination contains " + check_len + " sequential source letter(s) '" + m.Groups[1].Value + "'");
            m = m.NextMatch();
        }
        return bOK;
    }
}

欢迎基准测试...

相关文章: