2013-03-30 163 views
0

我试图对注册表单进行验证,以检查用户名/姓名+姓是否已经存在。检查行是否已存在

这是我的尝试:

Dim conn As New SqlConnection("Data Source=BRIAN-PC\SQLEXPRESS;Initial Catalog=master_db;Integrated Security=True") 
    Dim registerSQL As SqlCommand 
    Dim checkCredentialsSQL As SqlCommand 
    Dim sqlComm As String 
    Dim sqlCommName As String 
    Dim sqlCommUsername As String 

    sqlComm = "INSERT INTO users(Username, Password, Name, Surname, Address1, Address2, " + 
     "City, Country, date_of_birth, age, Occupation, department, work_location, " + 
     "project_manager,team_leader, team_leader_id, project_manager_id, " + 
     "date_registration, contract_type, contract_duration) " + 
     "VALUES(@p1, @p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p10,@p11,@p12,@p13,@p14,@p15," + 
     "@p16,@p17,@p18,@p19,@p20)" 

    sqlCommName = "SELECT name, surname FROM users WHERE name='" + txtName.Text + "' and surname='" + txtSurname.Text + "'" 
    sqlCommUsername = "SELECT username FROM users WHERE username='" + txtUsername.Text + "'" 

    conn.Open() 

    checkCredentialsSQL = New SqlCommand(sqlCommName, conn) 


    If checkCredentialsSQL.ExecuteScalar IsNot Nothing Then 

     lblName.Text = txtName.Text + " " + txtSurname.Text + "is already registered." 
     lblName.Visible = True 


    Else 

     checkCredentialsSQL = New SqlCommand(sqlCommUsername, conn) 

     If checkCredentialsSQL.ExecuteScalar IsNot Nothing Then 

      lblUsername.Text = "'" + txtUsername.Text + "' is already taken." 

     Else 


      registerSQL = New SqlCommand(sqlComm, conn) 
      registerSQL.Parameters.AddWithValue("@p1", Username) 
      registerSQL.Parameters.AddWithValue("@p2", Password) 
      registerSQL.Parameters.AddWithValue("@p3", Name) 
      registerSQL.Parameters.AddWithValue("@p4", Surname) 
      registerSQL.Parameters.AddWithValue("@p5", Address1) 
      registerSQL.Parameters.AddWithValue("@p6", Address2) 
      registerSQL.Parameters.AddWithValue("@p7", City) 
      registerSQL.Parameters.AddWithValue("@p8", Country) 
      registerSQL.Parameters.AddWithValue("@p9", DOB) 
      registerSQL.Parameters.AddWithValue("@p10", Age) 
      registerSQL.Parameters.AddWithValue("@p11", Occupation) 
      registerSQL.Parameters.AddWithValue("@p12", Department) 
      registerSQL.Parameters.AddWithValue("@p13", WorkLocation) 
      registerSQL.Parameters.AddWithValue("@p14", ProjectManager) 
      registerSQL.Parameters.AddWithValue("@p15", TeamLeader) 
      registerSQL.Parameters.AddWithValue("@p16", TeamLeaderID) 
      registerSQL.Parameters.AddWithValue("@p17", ProjectManagerID) 
      registerSQL.Parameters.AddWithValue("@p18", RegistrationDate) 
      registerSQL.Parameters.AddWithValue("@p19", ContractType) 
      registerSQL.Parameters.AddWithValue("@p20", ContractDuration) 

      registerSQL.ExecuteNonQuery() 



     End If 

    End If 

    conn.Close() 

是否可行/安全/建议做它呢?

+1

我个人将所有3这些功能放入SQL Server中的单个存储过程。 –

+0

为什么你有一个命令的参数,并为其他人使用字符串连接?你已经知道以这种方式传递字符串并不安全 – Steve

+0

@MartinParkin我仍然可以在页面上输出一个“错误消息”?你有什么好的教程? – Brian

回答

0

sqlCommName命令不针对SQL注入进行保护。如果txtName.Text包含existing_username AND 1 = 0,则用户可以注册两次。

0

虽然我与你正在使用的(因为名字+姓几乎肯定一个人的唯一标识符)的标准不一致,一般形式应该是一个尝试INSERT

INSERT INTO TabA (Val1,Val2,Val3) 
SELECT @Val1,@Val2,@Val3 
WHERE NOT EXISTS (SELECT * FROM TabA where Val1 = @Val1 or (Val2 = @Val2 and Val3 = @Val3) 

否则,你仍然在暴露自己的竞争条件。

而且,这取决于并发设置你下运行,你可以仍然需要处理违反约束(我假设你强制你做任何唯一性约束有羽绒在数据库级别上也有)

1

下面是一个简单的存储过程,检查要检查(下文有更多介绍)两个条件:

CREATE PROCEDURE dbo.CreateUser 
    @username NVARCHAR(255), 
    @name  NVARCHAR(64), 
    @surname NVARCHAR(64) 
    /* ... other params ... */ 
AS 
BEGIN 
    SET NOCOUNT ON; 

    IF EXISTS (SELECT 1 FROM dbo.users WHERE username = @username) 
    BEGIN 
    RAISERROR('Username %s is already registered.', 11, 1, @username); 
    RETURN -1; 
    END 

    IF EXISTS (SELECT 1 FROM dbo.users WHERE name = @name AND surname = @surname) 
    BEGIN 
    RAISERROR('Name %s %s is already registered.', 11, 1, @name, @surname); 
    RETURN -2; 
    END 

    BEGIN TRY 
    INSERT dbo.Users(username, name, surname, ...other columns...) 
      SELECT @username, @name, @suername, ...other params...; 
    END TRY 
    BEGIN CATCH 
    DECLARE @msg NVARCHAR(255) = ERROR_MESSAGE(); 
    RAISERROR(@msg, 11, 1); 
    RETURN -3; 
    END CATCH 
END 

现在你VB.Net代码简单得多:

Dim conn As New SqlConnection("...conn string...") 
conn.Open() 
Dim sql As New SqlCommand("dbo.CreateUser", conn) 
sql.CommandType = CommandType.StoredProcedure 
rv = sql.Parameters.Add("@rv", SqlDbType.Int) 
rv.Direction = ParameterDirection.ReturnValue  

sql.Parameters.AddWithValue("@username", Username) 
sql.Parameters.AddWithValue("@name",  Name) 
sql.Parameters.AddWithValue("@surname", Surname) 
... other params ... 

sql.ExecuteNonQuery() 
Debug.Print "Return value: " & rv.ToString 

...act accordingly depending on error raised and/or return value  
...maybe you want TRY/CATCH here? 

conn.Close() 

尝试插入(使用WHERE子句,或只是让约束引发错误)可能是一种技术上可行的方法,但我认为这不是一个好主意。第一,因为你的代码不能告诉你哪些条件引起的问题,第二个,因为这是非常昂贵,让SQL服务器引发异常你:

http://www.sqlperformance.com/2012/08/t-sql-queries/error-handling

http://www.mssqltips.com/sqlservertip/2632/checking-for-potential-constraint-violations-before-entering-sql-server-try-and-catch-logic/

那并不当然,你不应该有潜在的限制。他们应该在那里的情况下,用户不通过您的存储过程,应用程序层等。

哦,我同意其他人。你真的认为只有一个约翰史密斯会注册吗?与其他人拥有相同的名字并不是阻止某人注册IMHO的非常有效的理由。


PS我很抱歉,如果我的VB.Net代码是远远不够完善。我不写在VB和一些这是一个有教育的猜测...

+0

你的VB代码写得很好。它详细解释了如何在SQL中引发错误并在.NET中抓取它。 – highwingers