Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/gwt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如果5次尝试失败,则锁定用户_C#_Sql_Asp.net Mvc - Fatal编程技术网

C# 如果5次尝试失败,则锁定用户

C# 如果5次尝试失败,则锁定用户,c#,sql,asp.net-mvc,C#,Sql,Asp.net Mvc,我已经可以减少数据库中设置为5的AttemptsLeft。如果我用正确的用户名输入了错误的密码,AttemptsLeft将减少1。如果用户的状态为挂起,我还可以让用户不登录系统 问题: 我将AttemptsLeft设置为0时,用户的状态将变为Suspended。但是,数据库中的AttemptsLeft总是会减少,用户的状态不会变为Suspended,但是如果我正确输入用户名和密码,无论AttemptsLeft剩下多少,它都会变为Suspended 有什么不对劲吗 我认为从数据库中检索的user.

我已经可以减少数据库中设置为
5
AttemptsLeft
。如果我用正确的用户名输入了错误的密码,
AttemptsLeft
将减少1。如果用户的状态为
挂起
,我还可以让用户不登录系统

问题:

我将
AttemptsLeft
设置为0时,用户的状态将变为
Suspended
。但是,数据库中的
AttemptsLeft
总是会减少,用户的状态不会变为
Suspended
,但是如果我正确输入用户名和密码,无论
AttemptsLeft
剩下多少,它都会变为
Suspended

有什么不对劲吗

我认为从数据库中检索的
user.Attempts
不起作用,这是因为
user.Attempts
总是
0
,只有数据库中的
AttemptsLeft
会减少,因为我通过
query
来减少它

以下是我正在使用的代码:

用户管理器类:

登录文本模型:

控制器:


如果您遵循这样的场景,即当用户无效时,它只会减少无效尝试。除非用户正确输入登录凭据,否则它实际上不会挂起用户

实际上,你的sudo逻辑应该是:

  • 验证凭据
  • 凭据有效
  • 检查帐户状态,然后拒绝访问
  • 将尝试次数重置回5次
  • 允许用户进入应用程序
  • 凭据无效
  • 将尝试次数减少一次
  • 如果需要,将帐户设置为暂停
  • 许多这种逻辑实际上可以绑定到一个或两个方法中。在
    decreaseTempts()
    方法中,还有两个SQL命令,其中SQL命令
    cmd
    从未执行过

    下面是一个在返回状态枚举的方法中执行此操作的示例。这是一个非常基本的示例,但只需要一个方法来执行完全授权方法。我已经对代码进行了注释

    public partial class UserManager 
    {
    
        const int MaxAttempts = 5;
    
        public LoginStatus ValidateUser(string username, string password)
        {
            if (string.IsNullOrWhiteSpace(username))
                throw new ArgumentNullException("username");
    
            //set the password to empty if it is null
            password = password ?? "";
    
            //create the connection
            using (var connection = new SqlConnection(Configuration.ConnectionString))
            {
                //assign some local variables
                int attemptsLeft = MaxAttempts;
                string currentStatus = "Active";
                string userPassword = null;
    
                //get the information for the user, only query by username so we have all the data. We will match the password later on
                string query = "SELECT TOP(1) [Username], [Password], [AttemptsLeft], [CurrentStatus] FROM [Information] WHERE Username = @username";
    
                using (var command = new SqlCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@username", username);
                    command.CommandType = System.Data.CommandType.Text;
    
                    connection.Open();
    
                    using (var reader = command.ExecuteReader())
                    {
                        //no rows.. Invalid username
                        if (!reader.HasRows)
                        {
                            connection.Close();
                            return LoginStatus.InvalidCredentials;
                        }
    
                        //read the first row (hence the break)
                        while (reader.Read())
                        {
                            attemptsLeft = (int)reader["AttemptsLeft"];
                            currentStatus = (string)reader["CurrentStatus"];
                            userPassword = (string)reader["Password"];
                            break;
                        }
                        reader.Close();
                    }
                    connection.Close();
                }
    
                //if the account is suspended then dont even bother with password checking
                if (currentStatus.Equals("Suspended", StringComparison.CurrentCultureIgnoreCase))
                {
                    return LoginStatus.Suspended;
                }
    
                //invalid password lets handle the invalid credentials logic
                if (!password.Equals(userPassword))
                {
                    attemptsLeft -= 1;
    
                    //decrease the attempts, lets just stop at zero as we dont need negative attempts
                    if(attemptsLeft >= 0)
                    {
                        query = "UPDATE [Information] SET [AttemptsLeft] = @attemptsLeft WHERE Username = @username";
                        using (var command = new SqlCommand(query, connection))
                        {
                            command.Parameters.AddWithValue("@username", username);
                            command.Parameters.AddWithValue("@attemptsLeft", attemptsLeft);
                            connection.Open();
                            command.ExecuteNonQuery();
                            connection.Close();
                        }
                    }
    
                    //suspend the account when attempts less than or equal to zero
                    if (attemptsLeft <= 0)
                    {
                        query = "UPDATE [Information] SET [CurrentStatus] = @currentStatus WHERE Username = @username";
                        using (var command = new SqlCommand(query, connection))
                        {
                            command.Parameters.AddWithValue("@username", username);
                            command.Parameters.AddWithValue("@currentStatus", "Suspended");
                            connection.Open();
                            command.ExecuteNonQuery();
                            connection.Close();
                        }
                        //exit method as login account suspended
                        return LoginStatus.Suspended;
                    }
    
                    //exit as invalid login credentials
                    return LoginStatus.InvalidCredentials;
                }
                //if we are here lets quickly reset the login attempts back to 5, and account status to active as this is a valid login
                query = "UPDATE [Information] SET [AttemptsLeft] = @attemptsLeft, [CurrentStatus] = @currentStatus WHERE Username = @username";
                using (var command = new SqlCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@username", username);
                    command.Parameters.AddWithValue("@attemptsLeft", MaxAttempts);
                    command.Parameters.AddWithValue("@currentStatus", "Active");
                    connection.Open();
                    command.ExecuteNonQuery();
                    connection.Close();
                }
                //if we got here then every thing is a match
                return LoginStatus.Authorized;
            }
        }
    
    }
    
    public enum LoginStatus
    {
        Authorized,
        InvalidCredentials,
        Suspended
    }
    
    只是为了好玩,我把它重写成了一个简单的存储过程

    CREATE PROCEDURE ValidateUser
        @username nvarchar(50),
        @password nvarchar(50)
    AS
    BEGIN
    
        SET NOCOUNT ON;
    
        DECLARE @userPassword nvarchar(50) = NULL
        DECLARE @maxAttempts int  = 5
        DECLARE @attemptsLeft int = 5
        DECLARE @currentStatus nvarchar(50)
    
        /*
            RETURN CODES:
            0 = Authorized
            1 = InvalidCredentials
            2 = Suspended
        */
    
    
        SELECT TOP(1) @userPassword = [UserName], @attemptsLeft = [AttemptsLeft], @currentStatus = [CurrentStatus] FROM [Information] WHERE UserName = @username
    
        IF @userPassword IS NULL
            BEGIN
                SELECT 1 as [Result], @maxAttempts as [AttemptsRemaining]
                RETURN
            END
    
        --account suspended.. Return a suspended result
        If @currentStatus = 'Suspended'
            BEGIN
                SELECT 2 as [Result], 0 as [AttemptsRemaining]
                RETURN
            END
    
        --passwords dont match (note this is case insensitive on default collation)
        If @password IS NULL OR @password <> @userPassword
            BEGIN
                --decrease attempts
                SET @attemptsLeft = @attemptsLeft - 1
    
                --if the attempts left are greater than 0 then set the account active and decrease the attempts remaining
                IF @attemptsLeft > 0
                    BEGIN
                        UPDATE [Information] SET [CurrentStatus] = 'Active', AttemptsLeft = @attemptsLeft WHERE UserName = @username
                        SELECT 1 as [Result], @attemptsLeft as [AttemptsRemaining]
                        RETURN
                    END
                --else the attempts left are less than or equal to zero therefore they should be suspended and attempts left set to zero (dont want negative attempts)
                ELSE
                    BEGIN
                        UPDATE [Information] SET [CurrentStatus] = 'Suspended', AttemptsLeft = 0 WHERE UserName = @username
                        SELECT 2 as [Result], 0 as [AttemptsRemaining]
                        RETURN
                    END
            END
        --if we get here then all is good and we can just reset the account status and max attempts for the next login attempt
        UPDATE [Information] SET [CurrentStatus] = 'Active', AttemptsLeft = @maxAttempts WHERE UserName = @username
        SELECT 0 as [Result], @maxAttempts AS [AttemptsRemaining]
    
    END
    GO
    
    结果类

    public class LoginResult
    {
        public LoginStatus Status { get; set; }
    
        public int AttemptsRemaining { get; set; }
    }
    
    public enum LoginStatus : int
    {
        Authorized = 0,
        InvalidCredentials = 1,
        Suspended = 2
    }
    
    控制器

    [HttpPost]
    public ActionResult Index(string username, string password)
    {
        if (string.IsNullOrWhiteSpace(username))
        {
            this.ModelState.AddModelError("", "Invalid Login Credential. No username sent.");
            return View();
        }
        var manager = new UserManager();
        var result = manager.ValidateUserStoredProcedure(username, password);
        switch (result.Status)
        {
            case LoginStatus.Authorized:
                return RedirectToAction("About", "Home");
    
            case LoginStatus.InvalidCredentials:
                if (result.AttemptsRemaining < 5)
                    this.ModelState.AddModelError("", "Invalid Login Credentials. Username or password incorrect. Attempts remaining:" + result.AttemptsRemaining);
                else
                    this.ModelState.AddModelError("", "Invalid Login Credentials. Username or password incorrect.");
                break;
    
            case LoginStatus.Suspended:
                this.ModelState.AddModelError("", "Account Suspeneded");
                break;
        }
        return View();
    }
    
    [HttpPost]
    公共操作结果索引(字符串用户名、字符串密码)
    {
    if(string.IsNullOrWhiteSpace(用户名))
    {
    this.ModelState.AddModelError(“,”无效的登录凭据。未发送用户名。“);
    返回视图();
    }
    var manager=new UserManager();
    var result=manager.validateUserStoredProcess(用户名、密码);
    开关(结果状态)
    {
    案例登录状态。授权:
    返回操作(“关于”、“家”);
    案例登录状态。无效凭证:
    如果(result.AttemptsRemaining<5)
    this.ModelState.AddModelError(“,”无效的登录凭据。用户名或密码不正确。剩余尝试次数:“+result.AttemptsRemaining”);
    其他的
    this.ModelState.AddModelError(“,”无效的登录凭据。用户名或密码不正确。“);
    打破
    案例登录状态已暂停:
    this.ModelState.AddModelError(“,”帐户已暂停”);
    打破
    }
    返回视图();
    }
    

    如何优化取决于您,但此授权级别相当弱。它还显示您正在将密码存储为纯文本。但这是另一个主题。

    或者您可以使用内置的aspnet_成员资格提供程序为您完成所有这些。GetAttempts()创建并填充局部变量,然后再也不会返回它,因此,我不确定你想在那里完成什么。@JK:我试图不依赖ASP.Net成员身份,而试图让它由我自己完成p@TiesonT.:那么,我该怎么办?我需要
    用户。尝试
    将始终读取数据库中的
    AttemptsLeft
    列。以便系统始终检查
    user.Attempts
    的值是否正确。我面临的问题是因为
    user.Attempts
    始终为0。我认为
    GetAttempts()
    方法没有被执行。@Stainn它执行得很好,您可以在调试模式下看到断点。问题是,你没有用从数据库中提取的值做任何事情。是的,我知道有一个问题,
    Login
    代码太乱了,这不是正确的过程,太多的
    if
    语句和太多的检查--“method,我想在更新某些内容之前,我必须先选择它。我已更新以显示使用多个内联查询和示例存储过程方法的代码示例。谢谢。它可以工作!:D我刚刚打开了堆栈溢出的帐户。对此表示抱歉。
    [HttpPost]
    public ActionResult Index(string username, string password)
    {
        if(string.IsNullOrWhiteSpace(username))
        {
            this.ModelState.AddModelError("", "Invalid Login Credential. No username sent.");
            return View();
        }
    
        var manager = new UserManager();
    
        var result = manager.ValidateUser(username, password);
    
        switch (result)
        {
            case LoginStatus.Authorized:
                return RedirectToAction("About", "Home");
    
            case LoginStatus.InvalidCredentials:
                this.ModelState.AddModelError("", "Invalid Login Credentials. Username or password incorrect");
                break;
    
            case LoginStatus.Suspended:
                this.ModelState.AddModelError("", "Account Suspeneded");
                break;
        }
    
        return View();
    }
    
    CREATE PROCEDURE ValidateUser
        @username nvarchar(50),
        @password nvarchar(50)
    AS
    BEGIN
    
        SET NOCOUNT ON;
    
        DECLARE @userPassword nvarchar(50) = NULL
        DECLARE @maxAttempts int  = 5
        DECLARE @attemptsLeft int = 5
        DECLARE @currentStatus nvarchar(50)
    
        /*
            RETURN CODES:
            0 = Authorized
            1 = InvalidCredentials
            2 = Suspended
        */
    
    
        SELECT TOP(1) @userPassword = [UserName], @attemptsLeft = [AttemptsLeft], @currentStatus = [CurrentStatus] FROM [Information] WHERE UserName = @username
    
        IF @userPassword IS NULL
            BEGIN
                SELECT 1 as [Result], @maxAttempts as [AttemptsRemaining]
                RETURN
            END
    
        --account suspended.. Return a suspended result
        If @currentStatus = 'Suspended'
            BEGIN
                SELECT 2 as [Result], 0 as [AttemptsRemaining]
                RETURN
            END
    
        --passwords dont match (note this is case insensitive on default collation)
        If @password IS NULL OR @password <> @userPassword
            BEGIN
                --decrease attempts
                SET @attemptsLeft = @attemptsLeft - 1
    
                --if the attempts left are greater than 0 then set the account active and decrease the attempts remaining
                IF @attemptsLeft > 0
                    BEGIN
                        UPDATE [Information] SET [CurrentStatus] = 'Active', AttemptsLeft = @attemptsLeft WHERE UserName = @username
                        SELECT 1 as [Result], @attemptsLeft as [AttemptsRemaining]
                        RETURN
                    END
                --else the attempts left are less than or equal to zero therefore they should be suspended and attempts left set to zero (dont want negative attempts)
                ELSE
                    BEGIN
                        UPDATE [Information] SET [CurrentStatus] = 'Suspended', AttemptsLeft = 0 WHERE UserName = @username
                        SELECT 2 as [Result], 0 as [AttemptsRemaining]
                        RETURN
                    END
            END
        --if we get here then all is good and we can just reset the account status and max attempts for the next login attempt
        UPDATE [Information] SET [CurrentStatus] = 'Active', AttemptsLeft = @maxAttempts WHERE UserName = @username
        SELECT 0 as [Result], @maxAttempts AS [AttemptsRemaining]
    
    END
    GO
    
    public LoginResult ValidateUserStoredProcedure(string username, string password)
    {
        if (string.IsNullOrWhiteSpace(username))
            throw new ArgumentNullException("username");
    
        //set the password to empty if it is null
        password = password ?? "";
    
        //create the connection
        using (var connection = new SqlConnection(Configuration.ConnectionString))
        {
            var result = new LoginResult
            {
                AttemptsRemaining = 5,
                Status = LoginStatus.InvalidCredentials
            };
            try
            {
                using (var command = new SqlCommand("EXEC ValidateUser @username, @password", connection))
                {
                    command.Parameters.AddWithValue("@username", username);
                    command.Parameters.AddWithValue("@password", password);
                    command.CommandType = System.Data.CommandType.Text;
                    connection.Open();
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            result.Status = ((LoginStatus)(int)reader["Result"]);
                            result.AttemptsRemaining = (int)reader["AttemptsRemaining"];
                            break;
                        }
                        reader.Close();
                    }
                    connection.Close();
                }
                return result;
            }
            catch (Exception ex)
            {
                if (connection.State != System.Data.ConnectionState.Closed)
                    connection.Close();
    
                Debug.WriteLine("Error on sql query:" + ex.Message);
                return result;
            }
        }
    }
    
    public class LoginResult
    {
        public LoginStatus Status { get; set; }
    
        public int AttemptsRemaining { get; set; }
    }
    
    public enum LoginStatus : int
    {
        Authorized = 0,
        InvalidCredentials = 1,
        Suspended = 2
    }
    
    [HttpPost]
    public ActionResult Index(string username, string password)
    {
        if (string.IsNullOrWhiteSpace(username))
        {
            this.ModelState.AddModelError("", "Invalid Login Credential. No username sent.");
            return View();
        }
        var manager = new UserManager();
        var result = manager.ValidateUserStoredProcedure(username, password);
        switch (result.Status)
        {
            case LoginStatus.Authorized:
                return RedirectToAction("About", "Home");
    
            case LoginStatus.InvalidCredentials:
                if (result.AttemptsRemaining < 5)
                    this.ModelState.AddModelError("", "Invalid Login Credentials. Username or password incorrect. Attempts remaining:" + result.AttemptsRemaining);
                else
                    this.ModelState.AddModelError("", "Invalid Login Credentials. Username or password incorrect.");
                break;
    
            case LoginStatus.Suspended:
                this.ModelState.AddModelError("", "Account Suspeneded");
                break;
        }
        return View();
    }