我正在尝试创建一个登录表单,该表单使用存储过程检查用户是否存在,并使用VS2015& amp ;;访问系统vb.net。 SQL Server 2012。
我创建了下表
CREATE TABLE dbo.[User]
(
UserID INT IDENTITY(1,1) NOT NULL,
LoginName NVARCHAR(40) NOT NULL,
PasswordHash BINARY(64) NOT NULL,
FirstName NVARCHAR(40) NULL,
LastName NVARCHAR(40) NULL,
CONSTRAINT [PK_User_UserID] PRIMARY KEY CLUSTERED (UserID ASC)
)
并运行以下内容以更新表
ALTER TABLE dbo.[User] ADD Salt UNIQUEIDENTIFIER
GO
ALTER PROCEDURE dbo.uspAddUser
@pLogin NVARCHAR(50),
@pPassword NVARCHAR(50),
@pFirstName NVARCHAR(40) = NULL,
@pLastName NVARCHAR(40) = NULL,
@responseMessage NVARCHAR(250) OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE @salt UNIQUEIDENTIFIER=NEWID()
BEGIN TRY
INSERT INTO dbo.[User] (LoginName, PasswordHash, Salt, FirstName, LastName)
VALUES(@pLogin, HASHBYTES('SHA2_512', @pPassword+CAST(@salt AS NVARCHAR(36))), @salt, @pFirstName, @pLastName)
SET @responseMessage='Success'
END TRY
BEGIN CATCH
SET @responseMessage=ERROR_MESSAGE()
END CATCH
END
我创建了一个带有用户和密码框提交按钮的简单表单我在提交按钮上使用以下代码
Private Sub RadButtonSubmit_Click(sender As Object, e As EventArgs) Handles RadButtonSubmit.Click
Dim dt As New DataTable()
Dim adp As New SqlDataAdapter()
Try
Dim con As New SqlConnection(My.Settings.Connection)
Dim cmd As New SqlCommand("uspLogin", con)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("@pLoginName", RadTextBoxUser.Text.Trim())
cmd.Parameters.AddWithValue("@pPassword", RadTextBoxPass.Text.Trim())
adp.SelectCommand = cmd
adp.Fill(dt)
cmd.Dispose()
If dt.Rows.Count > 0 Then
RadLabelMessage.Text = "Login Successfull"
'Or in show messagebox using ScriptManager.RegisterStartupScript(this, this.GetType(), "Message", "alert('Login Successfull');", true);
'Or write using Response.Write("Login Successfull");
'Or redirect using Response.Redirect("Mypanel.aspx");
Else
RadLabelMessage.Text = "Wrong Username/Password"
'Or show in messagebox usingScriptManager.RegisterStartupScript(this, this.GetType(), "Message", "alert('Wrong Username/Password');", true);
'Or write using Response.Write("Wrong Username/Password");
End If
Catch ex As Exception
'RegisterStartupScript(Me, Me.[GetType](), "Message", "alert('Oops!! following error occured : " & ex.Message.ToString() & "');", True)
' Response.Write("Oops!! following error occured: " +ex.Message.ToString());
Finally
dt.Clear()
dt.Dispose()
adp.Dispose()
End Try
End Sub
当我输入用户名和密码时,它始终显示“用户名/密码错误”
我使用
添加了用户DECLARE @responseMessage NVARCHAR(250)
EXEC dbo.uspAddUser
@pLogin = N'Admin',
@pPassword = N'123',
@pFirstName = N'Admin',
@pLastName = N'Administrator',
@responseMessage=@responseMessage OUTPUT
SELECT *
FROM [dbo].[User]
这是登录的存储过程
CREATE PROCEDURE dbo.uspLogin
@pLoginName NVARCHAR(254),
@pPassword NVARCHAR(50),
@responseMessage NVARCHAR(250)='' OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE @userID INT
IF EXISTS (SELECT TOP 1 UserID FROM [dbo].[User] WHERE LoginName=@pLoginName)
BEGIN
SET @userID=(SELECT UserID FROM [dbo].[User] WHERE LoginName=@pLoginName AND PasswordHash=HASHBYTES('SHA2_512', @pPassword+CAST(Salt AS NVARCHAR(36))))
IF(@userID IS NULL)
SET @responseMessage='Incorrect password'
ELSE
SET @responseMessage='User successfully logged in'
END
ELSE
SET @responseMessage='Invalid login'
END
我可以在我的数据库中看到用户存在关于如何解决此问题的任何建议
由于 中号
我使用以下代码重新创建了登录表单
Imports System.Net.Sockets
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text.RegularExpressions
Imports System.Data.SqlClient
Public Class Login
Private FirstNameValid As Boolean 'Is Name Valid?
Private LastNameValid As Boolean 'Is Surname Valid?
Private XUserNameValid As Boolean
Private UserPassValid As Boolean
Private EmailValid As Boolean 'Is Email Valid?
Private Sub FirstName_Leave(sender As Object, e As System.EventArgs) Handles FirstName.Leave
If FirstName.Text = "" Then ErrorLabel.Text = "Please Enter Your First Name!"
'If Not A Matching Format Entered
If Not Regex.Match(FirstName.Text, "^[a-z]*$", RegexOptions.IgnoreCase).Success Then 'Only Letters
ErrorLabel.Text = "Please Enter Alphabetic Characters Only!" 'Inform User
FirstName.Focus() 'Return Focus
FirstName.Clear() 'Clear TextBox
FirstNameValid = False 'Boolean = False
Else
FirstNameValid = True 'Everything Fine
ErrorLabel.Text = ""
End If
End Sub
Private Sub LastName_Leave(sender As Object, e As System.EventArgs) Handles LastName.Leave
'Create A Pattern For Surname
Dim strSurname As String = "^[a-zA-Z\s]+$"
Dim reSurname As New Regex(strSurname) 'Attach Pattern To Surname Textbox
'Not A Match
If Not reSurname.IsMatch(LastName.Text) Then
ErrorLabel.Text = "Please Enter Alphabetic Characters Only!"
LastName.Focus()
LastName.Clear()
LastNameValid = False
Else
LastNameValid = True
ErrorLabel.Text = ""
End If
End Sub
Private Sub UserPass_Leave(sender As Object, e As System.EventArgs) Handles UserPass.Leave
If UserPass.Text = "" Then ErrorLabel.Text = "Please Enter Your Password!"
If UserPass.Text.Length < "8" Then
ErrorLabel.Text = "Password must be 8 Charecters!"
UserPass.Focus()
UserPass.Clear()
UserPassValid = False
Else
UserPassValid = True
ErrorLabel.Text = ""
End If
End Sub
Private Sub ValidateEmail()
'Set Up Reg Exp Pattern To Allow Most Characters, And No Special Characters
Dim reEmail As Regex = New Regex("([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\." +
")|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})",
RegexOptions.IgnoreCase _
Or RegexOptions.CultureInvariant _
Or RegexOptions.IgnorePatternWhitespace _
Or RegexOptions.Compiled
)
Dim blnPossibleMatch As Boolean = reEmail.IsMatch(UserEmail.Text)
If blnPossibleMatch Then
'Check If Entered Email Is In Correct Format
If Not UserEmail.Text.Equals(reEmail.Match(UserEmail.Text).ToString) Then
ErrorLabel.Text = "Invalid Email Address!"
Else
EmailValid = True 'Email is Perfect
End If
Else 'Not A Match To Pattern
EmailValid = False 'Set Boolean Variable To False
ErrorLabel.Text = "Invalid Email Address!" 'Inform User
UserEmail.Clear() 'Clear Textbox
UserEmail.Focus() 'Set Focus To TextBox
End If
End Sub
Private Sub txtEmail_LostFocus(sender As Object, e As System.EventArgs) Handles UserEmail.LostFocus
If UserEmail.Text = "" Then ErrorLabel.Text = "Invalid Email Address!"
ValidateEmail() 'Check Email Validity
End Sub
'The maximum number of times the user can try to login.
Private Const MAX_ATTEMPT_COUNT As Integer = 3
'The number of times the user has tried to login.
Private attemptCount As Integer = 0
Private Sub Login_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
RadPanel1.Show()
RadPanel2.Hide()
RadLabel7.Text = "Register"
TextBoxUserName.Focus()
Dim splash As SplashScreen1 = CType(My.Application.SplashScreen, SplashScreen1)
Dim MadeUpSteps() As String = {"Initializing...", "Loading ports...", "Checking Connection...", "Please wait...", "Connecting..."}
For i As Integer = 0 To MadeUpSteps.Length - 1
splash.UpdateProgress(MadeUpSteps(i), CInt((i + 1) / MadeUpSteps.Length * 100))
System.Threading.Thread.Sleep(1500)
Next
Dim tcpc As New TcpClient()
Try
tcpc.Connect("cloud01.smarthosting.co.uk", 80)
Return
Catch
If MessageBox.Show("Sorry No connection is available try again later.",
"Connection Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error) = Windows.Forms.DialogResult.OK Then
Me.DialogResult = Windows.Forms.DialogResult.Cancel
Me.Close()
End If
End Try
End Sub
Dim adminLogin As Boolean
Private Sub OK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK.Click
Dim x As New System.Security.Cryptography.SHA512CryptoServiceProvider()
Dim bs As Byte() = System.Text.Encoding.UTF8.GetBytes(TextBoxPassword.Text)
bs = x.ComputeHash(bs)
Dim s As New System.Text.StringBuilder()
For Each b As Byte In bs
s.Append(b.ToString("x2").ToLower())
Next
TextBoxPassword.Text = s.ToString()
Dim pass = TextBoxPassword.Text
If Me.ValidateCredentials Then
Dim Obj As New Main
adminLogin = False
My.Forms.Main.RadTextBox1.Text = adminLogin
Me.DialogResult = Windows.Forms.DialogResult.OK
ElseIf Me.ValidateCredentials1 Then
Dim Obj As New Main
adminLogin = True
My.Forms.Main.RadTextBox1.Text = adminLogin
Me.DialogResult = Windows.Forms.DialogResult.OK
Else
Me.attemptCount += 1
Dim message As String
If Me.attemptCount = MAX_ATTEMPT_COUNT Then
message = "The maximum number of failed logins has been reached." &
Environment.NewLine &
"The application will now exit."
Me.DialogResult = Windows.Forms.DialogResult.Abort
Else
message = "The provided credentials are incorrect." &
Environment.NewLine &
"Please try again."
Me.TextBoxPassword.Clear()
Me.TextBoxUserName.SelectAll()
Me.TextBoxUserName.Select()
End If
MessageBox.Show(message,
"Login Failed",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End If
End Sub
Private Sub Cancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel.Click
If MessageBox.Show("Are you sure you want to exit without logging in?",
"Confirm Exit",
MessageBoxButtons.YesNo,
MessageBoxIcon.None) = Windows.Forms.DialogResult.Yes Then
Me.DialogResult = Windows.Forms.DialogResult.Cancel
End If
End Sub
Private Function ValidateCredentials() As Boolean
Dim result As Boolean
result = Me.ValidateCredentialsByTableAdapter()
Return result
End Function
Private Function ValidateCredentialsByTableAdapter() As Boolean
Using adapter As New System3DataSetTableAdapters.UserTableAdapter
Return adapter.ScalarQuery(Me.TextBoxUserName.Text.Trim(),
Me.TextBoxPassword.Text).Value > 0
End Using
End Function
Private Function ValidateCredentials1() As Boolean
Dim result As Boolean
result = Me.ValidateCredentialsByTableAdapter1()
Return result
End Function
Private Function ValidateCredentialsByTableAdapter1() As Boolean
Using adapter As New System3DataSetTableAdapters.Admin8TableAdapter
Return adapter.ScalarQuery(Me.TextBoxUserName.Text.Trim(),
Me.TextBoxPassword.Text).Value > 0
End Using
End Function
Private Sub RadLabel7_Click(sender As Object, e As EventArgs) Handles RadLabel7.Click
If RadPanel1.Visible = True Then
RadLabel7.Text = "Login"
RadPanel2.Show()
RadPanel1.Hide()
ElseIf RadPanel2.Visible = True Then
RadLabel7.Text = "Register"
RadPanel1.Show()
RadPanel2.Hide()
End If
End Sub
Private Sub RadButton1_Click(sender As Object, e As EventArgs) Handles RadButton1.Click
Dim x As New System.Security.Cryptography.SHA512CryptoServiceProvider()
Dim bs As Byte() = System.Text.Encoding.UTF8.GetBytes(UserPass.Text)
bs = x.ComputeHash(bs)
Dim s As New System.Text.StringBuilder()
For Each b As Byte In bs
s.Append(b.ToString("x2").ToLower())
Next
UserPass.Text = s.ToString()
Dim pass = UserPass.Text
Dim myConnection As New System.Data.ConnectionState
Dim mySqlDataAdapter As New SqlDataAdapter("Select * from Users", myConnection)
Dim myDataSet As New DataSet()
Dim myDataRow As DataRow
' Create command builder. This line automatically generates the update commands for you, so you don't
' have to provide or create your own.
Dim mySqlCommandBuilder As New SqlCommandBuilder(mySqlDataAdapter)
' Set the MissingSchemaAction property to AddWithKey because Fill will not cause primary
' key & unique key information to be retrieved unless AddWithKey is specified.
mySqlDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
mySqlDataAdapter.Fill(myDataSet, "Users")
myDataRow = myDataSet.Tables("Users").NewRow()
myDataRow("ID") = "NewID"
myDataRow("xFirstName") = FirstName.Text
myDataRow("xLastName") = LastName.Text
myDataRow("xEmail") = UserEmail.Text
myDataRow("UserPass") = pass
myDataSet.Tables("Users").Rows.Add(myDataRow)
End Sub
End Class
Public Class PasswordHash
Public Const SALT_BYTE_SIZE As Integer = 24
Public Const HASH_BYTE_SIZE As Integer = 24
Public Const PBKDF2_ITERATIONS As Integer = 1000
Public Const ITERATION_INDEX As Integer = 0
Public Const SALT_INDEX As Integer = 1
Public Const PBKDF2_INDEX As Integer = 2
Public Shared Function CreateHash(password As String) As String
' Generate a random salt
Dim csprng As New RNGCryptoServiceProvider()
Dim salt As Byte() = New Byte(SALT_BYTE_SIZE - 1) {}
csprng.GetBytes(salt)
' Hash the password and encode the parameters
Dim hash As Byte() = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE)
Return PBKDF2_ITERATIONS + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash)
End Function
''' <summary>
''' Validates a password given a hash of the correct one.
''' </summary>
''' <param name="password">The password to check.</param>
''' <param name="correctHash">A hash of the correct password.</param>
''' <returns>True if the password is correct. False otherwise.</returns>
Public Shared Function ValidatePassword(password As String, correctHash As String) As Boolean
' Extract the parameters from the hash
Dim delimiter As Char() = {":"c}
Dim split As String() = correctHash.Split(delimiter)
Dim iterations As Integer = Int32.Parse(split(ITERATION_INDEX))
Dim salt As Byte() = Convert.FromBase64String(split(SALT_INDEX))
Dim hash As Byte() = Convert.FromBase64String(split(PBKDF2_INDEX))
Dim testHash As Byte() = PBKDF2(password, salt, iterations, hash.Length)
Return SlowEquals(hash, testHash)
End Function
''' <summary>
''' Compares two byte arrays in length-constant time. This comparison
''' method is used so that password hashes cannot be extracted from
''' on-line systems using a timing attack and then attacked off-line.
''' </summary>
''' <param name="a">The first byte array.</param>
''' <param name="b">The second byte array.</param>
''' <returns>True if both byte arrays are equal. False otherwise.</returns>
Private Shared Function SlowEquals(a As Byte(), b As Byte()) As Boolean
Dim diff As UInteger = CUInt(a.Length) Xor CUInt(b.Length)
Dim i As Integer = 0
While i < a.Length AndAlso i < b.Length
diff = diff Or CUInt(a(i) Xor b(i))
i += 1
End While
Return diff = 0
End Function
''' <summary>
''' Computes the PBKDF2-SHA1 hash of a password.
''' </summary>
''' <param name="password">The password to hash.</param>
''' <param name="salt">The salt.</param>
''' <param name="iterations">The PBKDF2 iteration count.</param>
''' <param name="outputBytes">The length of the hash to generate, in bytes.</param>
''' <returns>A hash of the password.</returns>
Private Shared Function PBKDF2(password As String, salt As Byte(), iterations As Integer, outputBytes As Integer) As Byte()
Dim pbkdf2__1 As New Rfc2898DeriveBytes(password, salt)
pbkdf2__1.IterationCount = iterations
Return pbkdf2__1.GetBytes(outputBytes)
End Function
End Class
答案 0 :(得分:0)
uspLogin存储过程将其结果作为@responseMessage输出参数返回,而不是作为结果集。因此,用于登录的.NET代码不应使用DataTable来获取结果;相反,它应该定义一个@responseMessage输出参数。
使用这样的代码:
Dim resultParam = cmd.Parameters.Add("@responseMessage", SqlDbType.NVarChar, 250)
resultParam.Direction = ParameterDirection.Output
cmd.ExecuteNonQuery()
Dim res = resultParam.Value.ToString()