Noticias solo para curiosos


Comprobar usuario y clave usando una base de datos


En este artículo te muestro cómo verificar si el nombre y la clave de un usuario son correctos, pero comprobando esos datos desde una base de datos de SQL Server.
También te dejo un programa que crea la base de datos y la tabla de ejemplo, además de añadir 4 usuarios de prueba, dos de ellos con las claves guardadas de forma normal y los otros dos en los que las claves se han guardado usando SHA1, para que de esa forma no se guarde en la base de datos el texto "normal".
En este artículo, el código de ejemplo es para la versión 2003 de Visual Studio .NET (tanto para Visual Basic como para Visual C#), aunque también es válido para Visual Studio 2005, pero en el caso de Visual Basic, el código de la versión 2005 es algo más simple.

Veamos qué es lo que pasa cuando se pulsa en el botón Aceptar, es decir, cuando se ha escrito el nombre del usuario y la clave y se va a a comprobar si son correctos esos datos.

Private Sub btnAceptar_Click( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnAceptar.Click

If comprobarUsuario(Me.txtUsuario.Text, Me.txtClave.Text) Then
Me.DialogResult = DialogResult.OK
Else
' Permitir varios intentos
veces = veces + 1
If veces < color="#0000ff">Then
Label1.Text = "Quedan " & (NumeroIntentos - veces) & " intentos"
Exit Sub
End If
Me.DialogResult = DialogResult.No
End If
Hide()
End Sub

Como puedes ver en el texto resaltado, lo que hago es llamar a una función que es la que se encarga de comprobar si ese nombre de usuario y esa clave son datos correctos.
Veamos que hace esa función, ya que lo que se hace en ella es la parte importante de este artículo.
Primero veamos el código y ahora te explico un poco más de lo que ya explican los comentarios que es lo que se hace en esa función.

' Función para comprobar si el acceso es correcto
Private Function comprobarUsuario( _
ByVal nombre As String, _
ByVal clave As String) As Boolean

' Conectar a la base de datos
Dim cnn As SqlConnection = Nothing
'
Try
' Conectar a la base de datos de SQL Server
' (la cadena debe estar inicializada previamente)
cnn = New SqlConnection(cadenaCnn)
cnn.Open()

' Definir la cadena que vamos a usar para comprobar
' si el usuario y el password son correctos.
' Utilizo parámetros para evitar inyección de código.
Dim sel As New System.Text.StringBuilder

' Usando COUNT(*) nos devuelve el total que coincide
' con lo indicado en el WHERE,
' por tanto, si la clave y el usuario son correctos,
' devolverá 1, sino, devolverá 0
sel.Append("SELECT COUNT(*) FROM Usuarios ")
sel.Append("WHERE Nombre = @Nombre AND Clave = @Clave")
' Definir el comando que vamos a ejecutar
Dim cmd As New SqlCommand(sel.ToString, cnn)
' Creamos los parámetros
cmd.Parameters.Add("@Nombre", SqlDbType.NVarChar, 50)
cmd.Parameters.Add("@Clave", SqlDbType.NVarChar, 40)
'
' Asignamos los valores recibidos como parámetro
cmd.Parameters("@Nombre").Value = nombre
cmd.Parameters("@Clave").Value = clave
'
' Ejecutamos la consulta
' ExecuteScalar devuelve la primera columna de la primera fila
' por tanto, devolverá el número de coincidencias halladas,
' que si es 1, quiere decir que el usuario y el password son correctos.
Dim t As Integer = CInt(cmd.ExecuteScalar())
' Cerramos la conexión
cnn.Close()
'
' Si el valor devuelto es cero
' es que no es correcto.
If t = 0 Then
Return False
End If

Catch ex As Exception
MessageBox.Show("ERROR al conectar a la base de datos: " & vbCrLf & _
ex.Message, "Comprobar usuario", MessageBoxButtons.OK, _
MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1)
Return False
Finally
If Not cnn Is Nothing Then
cnn.Dispose()
End If
End Try
'
' Si llega aquí es que todo ha ido bien
Return True
End Function



Como ya te he comentado, lo que hacemos es comprobar si ese usuario y esa clave son correctas.
Tanto el nombre del usuario como la clave, los pasamos como parámetros de la función. Y se buscarán en la base de datos tal y como los pasemos a esa función.
Para no complicarte porqué te digo esto, sigue leyendo y al final te lo aclaro.
Este código "supone" que accedemos a una base de datos que está indicada en la cadena de conexión cadenaCnn, y que en esa base de datos hay una tabla llamada Usuarios que al menos tiene dos campos, uno llamado Nombre que es del tipo nvarchar y que tiene una longitud de 50 caracteres y el otro llamado Clave que también es del tipo nvarchar y con una longitud de 40 caracteres.
Nota:
Por supuesto, para tu caso concreto tendrás que cambiar esos valores por los adecuados, pero eso... ¡debes saber hacerlo tú!

Lo que hago es crear una "consulta" (la cadena select, que es el texto resaltado) en la que le digo a la base de datos que cuente cuantos datos hay que coincidan con lo que le indico. Es decir, que compruebe cuantos usuarios hay que tengan el nombre y la clave que se indican. Como es de suponer, no debemos tener más de un usuario con la misma clave, si ese es el caso... entonces vamos mal...
Si ese usuario y esa clave son correctos, esa consulta devolverá un uno y si no son correctos, devolverá cero. En caso de que devuelva UNO es que es correcto, y si devuelve CERO es que no es correcto.

Para saber cuantos datos devuelve esa consulta, uso el método ExecuteScalar del objeto SqlCommand, y tal como está en el comentario del código, ese método devuelve la primera columna de la primera fila de lo indicado en la cadena de selección, y como lo que debe devolver esa cadena de selección es el número de "datos" que coincidan con lo que hay en la parte WHERE, pues resulta que ese valor es en realidad el total de datos, y como te he dicho hace un párrafo, si devuelve CERO es que no existe esa combinación de nombre/clave, por tanto esos datos no son correctos, por tanto, devolvemos un valor FALSO.

Lo que se comprueba es lo que está en la base de datos

Pues eso, que lo que se comprueba con el código anterior es lo que haya en la base de datos, es decir, en la base de datos el nombre del usuario está tal y como lo indicamos (esto suele ser así), y la clave también está como la indicamos, esto último NO DEBERÍA SER ASÍ, ¿por qué? pues por seguridad, ya que si la clave está en texto "normal", será más fácil "averiguarla". Para saber cómo "complicar" un poco la cosa, sigue leyendo.
Guardar los datos de la clave de forma encriptada

Una solución para que el valor de la clave no esté en texto normal, es encriptándola.
Yo suelo guardar las claves en formato SHA1 al estilo de como lo hace el propio ASP.NET.
¿Qué consigo con esto?
Pues no dejar las claves como texto normal y corriente, sino como una ristra de 40 valores hexadecimales, que no permitan saber que clave es.

¿Cómo encriptar la clave?

Yo tengo una utilidad para generar la clave SHA1 a partir de una cadena, (mas adelante publicare como funciona con lujos de detalles), y lo que hago es convertir la clave en el valor correspondiente de la encriptación SHA1 y eso es lo que guardo en la base de datos.
Si quieres hacer esto mismo con tus claves, el código que te he mostrado antes, al menos el del evento Click del botón, no puedes usarlo así, ya que lo que debes comprobar en la base de datos es el valor SHA1 correspondiente a la clave que hayan escrito en la caja de textos de la clave.
Este es el código modificado del evento Click del botón Aceptar, en el que se llama a la función que genera el valor SHA1 de la clave introducida.

Private Sub btnAceptar_Click( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnAceptar.Click
' Convertir a SHA1 la clave introducida
Dim claveSHA As String = Me.generarClaveSHA1(Me.txtClave.Text)
If comprobarUsuario(Me.txtUsuario.Text, claveSHA) Then
Me.DialogResult = DialogResult.OK
Else
' Permitir varios intentos
veces = veces + 1
If veces < color="#0000ff">Then

Label1.Text = "Quedan " & (NumeroIntentos - veces) & " intentos"
Exit Sub
End If
Me.DialogResult = DialogResult.No
End If
Hide()
End Sub

Y este es el código de la función generarClaveSHA1:
Private Function generarClaveSHA1(ByVal nombre As String) As String ' Crear una clave SHA1 como la generada por ' FormsAuthentication.HashPasswordForStoringInConfig File ' Adaptada del ejemplo de la ayuda en la descripción de SHA1 (Clase) Dim enc As New UTF8Encoding Dim data() As Byte = enc.GetBytes(nombre) Dim result() As Byte Dim sha As New SHA1CryptoServiceProvider ' This is one implementation of the abstract class SHA1. result = sha.ComputeHash(data) ' ' Convertir los valores en hexadecimal ' cuando tiene una cifra hay que rellenarlo con cero ' para que siempre ocupen dos dígitos. Dim sb As New StringBuilder For i As Integer = 0 To result.Length - 1 If result(i) < color="#0000ff">Then sb.Append("0") End If sb.Append(result(i).ToString("x")) Next ' Return sb.ToString.ToUpperEnd FunctionPara que ese código te funcione debes tener una importación al espacio de nombres System.Security.Cryptography que es donde se define la clase SHA1CryptoServiceProvider y también a System.Text que es donde se definen las clases StringBuilder y UTF8Encoding.

Espero que todo lo aquí dicho te sea de utilidad y te sirva para hacer eso que querías saber y que tantas veces me han preguntado...

Un poco más abajo tienes el código completo de esta utilidad, en la que hay dos formularios para comprobar la clave, uno que no usa la clave SHA1 y otro que si lo usa.
También te dejo una utilidad para crear la base de datos de ejemplo y la tabla Usuarios con cuatro valores, dos de ellos usando texto normal y otros dos con las mismas claves, pero guardadas como una ristra de valores que corresponden con la cadena generada por la función generarClaveSHA1.

La utilidad para crear la base de datos y la tabla de usuarios de ejemplo:
http://www.megaupload.com/?d=NVD8W3ZO

El ejemplo completo para comprobar si el usuario y la clave son correctos:
http://www.megaupload.com/?d=L7H0SBAV




Espacios de nombres usados en el código de este artículo:

System.Windows.Forms
System.Drawing
System.Data.SqlClient
System.Security.Cryptography
System.Text

__________________
Si Tiene

Fashion

Beauty

Travel