繁体   English   中英

ASP.net中的表单身份验证

[英]Form Authentication in ASP.net

当天的问候! 我使用的是表单身份验证,在注册时可以,但是在登录时,它做错了,只是相反。 在“注册”中,假设我输入了密码123,现在它将使用表单身份验证转换该密码,并保存在DB中,现在在登录时,如果用户输入123,它将被更改,并尝试与数据库中存储的密码匹配。 就我而言,这是相反的,如果两个密码都匹配,则会显示自定义错误消息。如果不匹配,则增加锁定帐户的计数器变量

请仔细检查我的代码,然后帮帮我。

数据库:-

CREATE TABLE [dbo].[tblUsers](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [UserName] [nvarchar](15) NULL,
    [Password] [nvarchar](15) NULL,
    [Email] [nvarchar](200) NULL,
    [RetryAttempts] [int] NULL,
    [IsLocked] [bit] NULL,
    [LockedDateTime] [datetime] NULL,
PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]



CREATE proc [dbo].[sp_RegisterUser]
@UserName nvarchar(15),
@Password nvarchar(15),
@Email nvarchar(200)
As
Begin
Declare @Count int
Declare @ReturnCode int

Select @Count= COUNT(UserName) from tblUsers where UserName=@UserName
if(@Count=1)

Begin
Set @ReturnCode=-1
End

else
Begin
Set @ReturnCode=1
insert into tblUsers(UserName,Password,Email) values(@UserName,@Password,@Email)
End
Select @ReturnCode as ReturnValue
End


CREATE proc [dbo].[SP_AuthenticateUser]
@UserName nvarchar(15),
@Password nvarchar(15)
As
Begin
    Declare @Count int
    Declare @RetryCount int
    Declare @AccountLocked bit

    Select @AccountLocked= ISNULL(IsLocked,0)  from tblUsers where UserName=@UserName

    If(@AccountLocked=1)
    Begin
        Select 1 as AccountLocked,0 as Authenticate,0 as RetryAttempts
    End

    Else
    Begin
    Select @Count= COUNT(UserName) from tblUsers where UserName=@UserName and Password=@Password
    If(@Count=1)
    Begin
        Select 0 as AccountLocked,1 as Authenticate,0 as RetryAttempts
    End

    Else
    Begin
        Select @RetryCount=ISNULL(RetryAttempts,0) from tblUsers where UserName=@UserName
        Set @RetryCount=@RetryCount+1
    If(@RetryCount<=3)
    Begin
        Update tblUsers set RetryAttempts=@RetryCount where UserName=@UserName
        Select 0 as AccountLocked,0 as Authenticate,@RetryCount as RetryAttempts
    End
    Else
    Begin
        Update tblUsers set IsLocked=1,LockedDateTime=GETDATE() where UserName=@UserName
        Select 1 as AccountLocked,0 as Authenticate,0 as RetryAttempts
    End
    End
    End
End

设计:-

Registration_Page-

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Registration.aspx.cs" Inherits="Registration" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div style="font-family:Arial">
<table style="border: 1px solid black">
    <tr>
        <td colspan="2">
            <b>User Registration</b>
        </td>
    </tr>
    <tr>
        <td>
            User Name
        </td>    
        <td>
            :<asp:TextBox ID="txtUserName" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorusername" 
            runat="server" ErrorMessage="User Name required" Text="*"
            ControlToValidate="txtUserName" ForeColor="Red">
            </asp:RequiredFieldValidator>
        </td>    
    </tr>
    <tr>
        <td>
            Password
        </td>    
        <td>
            :<asp:TextBox ID="txtPassword" TextMode="Password" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorPassword" 
            runat="server" ErrorMessage="Password required" Text="*"
            ControlToValidate="txtPassword" ForeColor="Red">
            </asp:RequiredFieldValidator>
        </td>    
    </tr>
    <tr>
        <td>
            Confirm Password
        </td>    
        <td>
            :<asp:TextBox ID="txtConfirmPassword" TextMode="Password" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorConfirmPassword" 
            runat="server" ErrorMessage="Confirm Password required" Text="*"
            ControlToValidate="txtConfirmPassword" ForeColor="Red" 
            Display="Dynamic"></asp:RequiredFieldValidator>
            <asp:CompareValidator ID="CompareValidatorPassword" runat="server" 
            ErrorMessage="Password and Confirm Password must match"
            ControlToValidate="txtConfirmPassword" ForeColor="Red" 
            ControlToCompare="txtPassword" Display="Dynamic"
            Type="String" Operator="Equal" Text="*">
            </asp:CompareValidator>
        </td>    
    </tr>
    <tr>
        <td>
            Email
        </td>    
        <td>
            :<asp:TextBox ID="txtEmail" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorEmail" 
            runat="server" ErrorMessage="Email required" Text="*"
            ControlToValidate="txtEmail" ForeColor="Red"
            Display="Dynamic"></asp:RequiredFieldValidator>
            <asp:RegularExpressionValidator ID="RegularExpressionValidatorEmail" 
            runat="server" ErrorMessage="Invalid Email" ControlToValidate="txtEmail"
            ForeColor="Red" Display="Dynamic" Text="*"
            ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
            </asp:RegularExpressionValidator>
        </td>    
    </tr>
    <tr>
        <td>

        </td>    
        <td>
            <asp:Button ID="btnRegister" runat="server" Text="Register" 
            onclick="btnRegister_Click"/>
        </td>    
    </tr>
    <tr>
        <td colspan="2">
            <asp:Label ID="lblMessage" runat="server" ForeColor="Red">
            </asp:Label>
        </td>    
    </tr>
    <tr>
        <td colspan="2">
            <asp:ValidationSummary ID="ValidationSummary1" ForeColor="Red" runat="server" />
        </td>    
    </tr>
</table>
</div>
    </form>
</body>
</html>

登录页面:_

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div style="font-family:Arial">
<table style="border: 1px solid black">
    <tr>
        <td colspan="2">
            <b>Login</b>
        </td>
    </tr>
    <tr>
        <td>
            User Name
        </td>    
        <td>
            :<asp:TextBox ID="txtUserName" runat="server">
            </asp:TextBox>
        </td>    
    </tr>
    <tr>
        <td>
            Password
        </td>    
        <td>
            :<asp:TextBox ID="txtPassword" TextMode="Password" runat="server">
            </asp:TextBox>
        </td>    
    </tr>
    <tr>
        <td>
           <asp:CheckBox ID="chk_boxRememberMe" runat="server" Text="Remember Me" />         
        </td>    
        <td>
            <asp:Button ID="btnLogin" runat="server" Text="Login" OnClick="btnLogin_Click" />
        </td>    
    </tr>
    <tr>
        <td>
            <asp:Label ID="lblMessage" runat="server" ForeColor="Red"></asp:Label>
        </td>
    </tr>
</table>
<br />
<a href="Registration/Registration.aspx">Click here to register</a> 
if you do not have a user name and password.
</div>

    </form>
</body>
</html>

背后的代码:

注册页面:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Security;
public partial class Registration : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void btnRegister_Click(object sender, EventArgs e)

    {
        if (Page.IsValid)
        {
            string CS = ConfigurationManager.ConnectionStrings["con"].ConnectionString;

            using (SqlConnection Conn=new SqlConnection(CS))
            {
                SqlCommand cmd = new SqlCommand("sp_RegisterUser",Conn);
                cmd.CommandType = CommandType.StoredProcedure;

                SqlParameter UserName = new SqlParameter("@UserName",txtUserName.Text);
                string EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "SHA1");
                SqlParameter Password = new SqlParameter("@Password", EncryptPassword);

                SqlParameter Email = new SqlParameter("@Email", txtEmail.Text);

                cmd.Parameters.Add(UserName);
                cmd.Parameters.Add(Password);
                cmd.Parameters.Add(Email);

                Conn.Open();

                int ReturnCode=(int) cmd.ExecuteScalar();

                if (ReturnCode==-1)
                {
                    lblMessage.Text = "User Name alredy exists";
                }
                else
                {
                    Response.Redirect("~/Login.aspx");
                }
            }
        }
    }
}

登录页面:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Security;


public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void btnLogin_Click(object sender, EventArgs e)
    {
        //Login_WebConfig();
        // Login_DataBase();

        if (AuthenticateUser(txtUserName.Text, txtPassword.Text))
        {
            FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, chk_boxRememberMe.Checked);
        }
        else
        {
            lblMessage.Text = "Invalid username/password";
        }
    }

    //protected void Login_WebConfig()
    //{
    //    if (FormsAuthentication.Authenticate(txtUserName.Text, txtPassword.Text))
    //    {
    //        FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, chk_boxRememberMe.Checked);
    //    }
    //    else
    //    {
    //        lblMessage.Text = "Invalid user name/password";
    //    }
    //}

    protected void Login_DataBase()
    {

    }

    private bool AuthenticateUser(string username, string password)
    {
        string CS = ConfigurationManager.ConnectionStrings["con"].ConnectionString;
        using (SqlConnection Conn = new SqlConnection(CS))
        {
            SqlCommand cmd = new SqlCommand("SP_AuthenticateUser", Conn);
            cmd.CommandType = CommandType.StoredProcedure;

            string EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(password, "SHA1");

            SqlParameter paramUserName = new SqlParameter("@UserName", username);
            SqlParameter paramPassword = new SqlParameter("@Password", EncryptPassword);

            cmd.Parameters.Add(paramUserName);
            cmd.Parameters.Add(paramPassword);
            Conn.Open();

            int ReturnCode = (int)cmd.ExecuteScalar();
            return ReturnCode == 1;


        }
    }
}

网络配置

<?xml version="1.0"?>

<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->

<configuration>

    <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
      <authentication mode="Forms">
        <forms loginUrl="Login.aspx" defaultUrl="Welcome.aspx" timeout="2" protection="All">
          <!--<credentials passwordFormat="Clear">
            <user name="rkbisht" password="1234"/>
          </credentials>-->
        </forms>
      </authentication>
      <authorization>
        <deny users="?"/>
      </authorization>
  </system.web>

    <appSettings>
      <add key="ValidationSettings:UnobtrusiveValidationMode" value="None" />
    </appSettings>

  <connectionStrings>
    <add name="con" connectionString="Data Source=.;Initial Catalog=Security_Learning;Integrated Security=true"/>
  </connectionStrings>

</configuration>

基本上,您没有使用正确的工具来完成工作。

您不是在正式使用FormsAuthentication ,也没有将哈希保存在配置文件中,因此FormsAuthentication.HashPasswordForStoringInConfig不是您应使用的格式。

值得花点时间来解决这个问题。 这是一个起点:

https://docs.microsoft.com/zh-cn/aspnet/core/security/data-protection/consumer-apis/password-hashing

注意,您应该使用哈希和盐,盐对于每个用户应该是唯一的。

我个人使用Microsoft.AspNetCore.Cryptography.KeyDerivation nuget包,其实现类似于以下内容:

助手类

using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Resources;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;

public static class SecurityHelper
{
     public static int DefaultIterations = 10000

     //KeyDerivation.Pbkdf2
     /// <summary>
     /// Generates a random salt
     /// </summary>
     /// <returns>A byte array containing a random salt</returns>
     public static byte[] GetRandomSalt()
     {
         byte[] saltBytes = new byte[32];
         RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
         rng.GetNonZeroBytes(saltBytes);            
         return saltBytes;
     }

     public static string GeneratePasswordHash(string plainPassword, int iterations, out string generatedRandomSalt)
     {
         generatedRandomSalt = Convert.ToBase64String(GetRandomSalt());
         return Convert.ToBase64String(ComputeHash(plainPassword, generatedRandomSalt, iterations));
     }

     public static string GetPasswordHash(string plainPassword, string existingSalt, int iterations)
     {
         return Convert.ToBase64String(ComputeHash(plainPassword, existingSalt, iterations));
     }



    private static byte[] ComputeHash(string plainText, string salt, int iterations)
    {
         return KeyDerivation.Pbkdf2(
         password: plainText,
            salt: Convert.FromBase64String(salt),
            prf: KeyDerivationPrf.HMACSHA256,
            iterationCount: iterations,
            numBytesRequested: 32);
    }       

 }

现在,您的工作流程将更改:

  • 保存密码时,还需要保存盐
  • 在对用户进行身份验证时,请使用数据库中的用户名作为用户名,使用保存的盐计算输入密码的哈希值,并将结果与​​保存的哈希值进行比较。

大致

//Registration
using (SqlConnection Conn=new SqlConnection(CS))
{
    SqlCommand cmd = new SqlCommand("sp_RegisterUser",Conn);
    cmd.CommandType = CommandType.StoredProcedure;

    SqlParameter UserName = new SqlParameter("@UserName",txtUserName.Text);
    string Salt = string.Empty;
    string Password = SecurityHelper.GeneratePasswordHash(txtPassword.Text, SecurityHelper.DefaultIterations, out salt);
    ;
    SqlParameter Password = new SqlParameter("@Password", Password);
    SqlParameter Email = new SqlParameter("@Email", txtEmail.Text);
    SqlParameter Salt = new SqlParameter("@Salt", Salt);
    cmd.Parameters.Add(UserName);
    cmd.Parameters.Add(Password);
    cmd.Parameters.Add(Email);

    Conn.Open();

    int ReturnCode=(int) cmd.ExecuteScalar();

    if (ReturnCode==-1)
    {
        lblMessage.Text = "User Name alredy exists";
    }
    else
    {
         Response.Redirect("~/Login.aspx");
    }
}

//Log In
private bool AuthenticateUser(string username, string password)
{
    //Get the following from your database based on username
    string savedHash = //fromDB;
    string savedSalt = //fromDb;

    return (SecurityHelper.GetPasswordHash(password, savedSalt, SecurityHelper.DefaultIterations) == tempUser.Password)                
}

我对此做了一些简化。 如果需要增加默认迭代次数,我还将针对用户的迭代次数保存在数据库中。

我的最后一点提法,因此请阅读一些有关为什么“盐腌”是一件好事的文章。

身份验证存储过程( SP_AuthenticateUser )返回3列,但是您正在使用ExecuteScalar对其进行调用。 您需要获取数据集并检查第二列。

private bool AuthenticateUser(string username, string password)
{
    string CS = ConfigurationManager.ConnectionStrings["con"].ConnectionString;
    using (SqlConnection Conn = new SqlConnection(CS))
    {
        SqlCommand cmd = new SqlCommand("SP_AuthenticateUser", Conn);
        cmd.CommandType = CommandType.StoredProcedure;

        string EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(password, "SHA1");

        SqlParameter paramUserName = new SqlParameter("@UserName", username);
        SqlParameter paramPassword = new SqlParameter("@Password", EncryptPassword);

        cmd.Parameters.Add(paramUserName);
        cmd.Parameters.Add(paramPassword);
        Conn.Open();

        var reader = cmd.ExecuteReader();
        reader.Read();
        return reader["Authenticate"] as bool;
    }
}

另外,请确保在成功通过身份验证后重置重试计数器。

Select @Count= COUNT(UserName) from tblUsers where UserName=@UserName and Password=@Password
If(@Count=1)
Begin
    Update tblUsers set RetryAttempts = 0 where UserName = @UserName
    Select 0 as AccountLocked,1 as Authenticate,0 as RetryAttempts
End

您的代码也有其他一些问题,但是这两个问题很可能导致问题中提到的行为。

这是散列密码的更好方法

除非您设置表单身份验证Cookie,否则用户将必须继续登录。 单击此处了解有关方法的想法

您可能不需要在web.config中使用凭据。 如果您打算使用FormsAuthentication.Authenticate ,则只需将它们放在此处。 您似乎正在使用数据库。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM