【问题标题】:Form Authentication in ASP.netASP.net 中的表单身份验证
【发布时间】:2017-11-02 04:51:47
【问题描述】:

今天的问候! 我正在使用表单身份验证,在注册时可以,但是在登录时,它做错了,它只是在做相反的事情。 在注册中,假设我输入了密码 123,现在它会使用表单验证转换该密码,并保存在数据库中,现在登录时,如果用户输入 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>

【问题讨论】:

  • 为什么要建立自己的身份验证系统?您希望您的网站遭到入侵吗?使用 ASP.NET Identity,或者如果你不能,它的前身 Membership。永远不要推出自己的身份验证。

标签: c# asp.net authentication


【解决方案1】:

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

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

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

https://docs.microsoft.com/en-us/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);
    }       

 }

您的工作流程现在发生了变化:

  • 保存密码时,还需要保存盐
  • 在对用户进行身份验证时,通过 UserName 从数据库中获取用户,计算输入密码的哈希值,使用保存的 salt,将结果与保存的哈希值进行比较。

大概

//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)                
}

我已经稍微简化了这一点。我还将针对用户的迭代保存在数据库中,以防我们需要增加默认迭代。

我的最后一点建议,请阅读一下为什么“加盐”是一件好事。

【讨论】:

    【解决方案2】:

    您的身份验证存储过程 (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
    

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

    Here is a better way to hash your password.

    除非您设置表单身份验证 cookie,否则用户必须继续登录。 Click here for ideas on how.

    您的 web.config 中可能不需要凭据。如果你打算使用FormsAuthentication.Authenticate,你只需要把它们放在那里。您似乎正在使用数据库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多