Autenticación de usuarios con ASP.NET

ASP.NET ofrece tres mecanismos de autenticación: Windows, Passport y Formularios. De estos tres me voy a dedicar a explicar autenticación con formularios en detalle y voy a hacer un breve comentario sobre los otros dos.

La autenticación Windows permite al programador delegar la responsabilidad de la autenticación a Windows y al sistema de archivos NTFS, aprovechando también las funcionalidades de seguridad integradas en IIS. Normalmente, cuando un cliente realiza una petición a una páginas ASP.NET o a un web service, estas peticiones son manejadas por IIS. Si configuramos la autenticación en modo “Windows”, IIS delegará la tarea al sistema operativo. El usuario será autenticado en base a las credenciales que proveyó cuando inició sesión en Windows.

La autenticación Passport utiliza un servicio centralizado provisto por Microsoft. Para utilizar Passport hay que registrarse en el servicio, aceptar el acuerdo de licencia, pagar la cuota del servicio e instalar el SDK de Passport.

Finalmente, el método del cual vamos a hablar con mayor detalle es autenticación por formularios. Este método ofrece muchas ventajas al desarrollador, siendo la flexibilidad una de las más importantes. Será responsabilidad del desarrollador diseñar el formulario de inicio de sesión, implementar algún tipo de repositorio para las credenciales de usuario y realizar la validación contra ese repositorio, entre otras cosas.

Para utilizar autenticación por formularios es necesario realizar la siguiente configuración en el archivo web.config:

<configuration>
    <system.web>
        <authentication mode=”Forms”>
            <forms name=”auth_cookie” loginUrl=”Login.aspx” />
        </authentication>

        <authorization>
            <deny users=”?” />
        </authorization>
    </system.web>
</configuration>

Noten que puede haber, además de esto, otras configuraciones actualmente presentes en el web.config.

Con esto, lo que hemos hecho es:

  1. Definir el modo de autenticación.
  2. Denegar la autorización de acceso a los usuarios anónimos (simbolizados con el signo “?”).

Veamos ahora cómo realizar una autenticación sencilla siguiendo los siguientes pasos:

1.Creemos una página de inicio Default.aspx simple, para validar el ejemplo.

2.Creemos una página de login sencilla como la siguiente:

`<form id="form1" runat="server">
<h1>Login</h1>
<asp:Panel ID="panelMensaje" runat="server" Visible="true" EnableViewState="False" Width="100%">
  <asp:Label ID="lblMensaje" runat="server"></asp:Label>
</asp:Panel>
<p>Usuario:<br />
  <asp:TextBox ID="txtUsuario" runat="server"></asp:TextBox> </p>
<p>Password:<br />
 <asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox>
</p>
<p><asp:Button ID="btnLogin" runat="server" Text="Login" OnClick="btnLogin_Click" /></p>
</form>`

3.Utilicemos el siguiente code-behind:

public partial class Login : System.Web.UI.Page
{
  private const string usuario = "jose";
  private const string password = "abc123";

  protected void Page_Load(object sender, EventArgs e)
  {

  }
  protected void btnLogin_Click(object sender, EventArgs e)
  {
      if (txtUsuario.Text == usuario && txtPassword.Text == password)
      {
          FormsAuthentication.RedirectFromLoginPage(txtUsuario.Text, false);
      }
      else
      {
          panelMensaje.Visible = true;
          lblMensaje.Text = string.Format(
            "Usuario {0} no autenticado. Intente nuevamente.", txtUsuario.Text);
      }
  }
}

Con este ejemplo, y el web.config que comentamos antes, debería redirigirnos a la página Login.aspx cuando quisiéramos acceder a nuestra Default.aspx.

Otros parámetros del tag <form> son los siguientes:

  • protection: indica si la aplicación debe usar validación, encriptación, ninguno o ambos métodos para proteger la cookie. El valor predeterminado es All, que utiliza ambos métodos. None indica ningún método. Encryption utiliza Triple-DES para encriptar la cookie y Validation especifica que se valide la información leída en la cookie antes de utilizarla.
  • timeout: expecifica el número de minutos de vida de la cookie.
  • slidingExpiration: valor true o false indicando si la cookie se refresca con cada petición. En ASP.NET 1.1, el valor predeterminad es true y en 2.0 es false.

Autenticación contra el web.config

El ejemplo anterior mostraba cómo autenticar usuarios contra datos fijos en el archivo de code-behind. Otra forma de guardar los datos de los usuarios es en el archivo code-behind.

<configuration>
    <system.web>
        <authentication mode=”Forms”>
            <forms name=”auth_cookie” loginUrl=”Login.aspx”>
                <credentials passwordFormat="Clear">
                    <user name="marcelo" password="abc123" />
                    <user name="pablo" password="zxy987" />
                </credentials>
            </forms>
        </authentication>

        <authorization>
            <deny users=”?” />
        </authorization>
    </system.web>
</configuration>

Guardar los passwords en formato “Clear” no es lo más recomendable. Es posible también guardarlos hasheados en MD5 y SHA1. Para hashear los passwords podemos utilizar el método HashPasswordForStoringInConfigFile() de la clase FormsAuthentication.

Para autenticar de este modo debemos utilizar el método Authenticate de la clase FormsAuthentication. Autenticación contra una base de datos

Este tal vez es el método más común. En este ejemplo se trabaja con una función Authenticate dentro de la página aunque lo más frecuente es tener toda la lógica de autenticación en algún componente separado.

protected void btnLogin_Click(object sender, EventArgs e)
{
    if (Authenticate(txtUsuario.Text, txtPassword.Text))
    {
        FormsAuthentication.RedirectFromLoginPage(txtUsuario.Text, false);
    }
    else
    {
        panelMensaje.Visible = true;
        lblMensaje.Text = string.Format(
            "Usuario {0} no autenticado. Intente nuevamente.", txtUsuario.Text);
    }
}

public bool Authenticate(string usuario, string password)
{
    // defino la consulta (uso parámetros para evitar sql injection
    string query = "select count(*) from usuarios where usuario=@usuario and password=@password";

    // defino la conexión
    SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["ConnectionString"]);

    // defino el command y le asigno los parámetros a la consulta.
    SqlCommand cmd = new SqlCommand(query, conn);
    cmd.Parameters.AddWithValue("@usuario", usuario);
    cmd.Parameters.AddWithValue("@password", password);

    // ejecuto la consulta.
    conn.Open();
    int count = (int)cmd.ExecuteScalar();
    conn.Close();

    // devuelvo true solo si obtuve 1 resultado
    return count == 1;
}

Redirigir a una página específica

Para redirigir a una página específica, distinta de la que podría venir en el parámetro RedirectUrl, primero enviamos la cookie y luego hacemos el redireccionamiento:

FormsAuthentication.SetAuthCookie(txtUsuario.Text, false);
Response.Redirect("OtraPagina.aspx");

Cerrar la sesión

FormsAuthentication.SignOut();

Locations

Por defecto, todos los directorios y subdirectorios, incluyendo archivos, donde se encuentre el web.config estarán protegidos al activar el mecanismo de autenticación. Si necesitamos que alguna ubicación específica tenga permisos diferentes, podemos (1) recurrir a un web.config por cada directorio o (2), mucho mejor, definir elementos en el web.config principal.

<location path="imagenes">
    <system.web>
        <authorization>
            <allow users="*" />
        </authorization>
    </system.web>
</location>

<location path="estilos.css">
    <system.web>
        <authorization>
            <allow users="*" />
        </authorization>
    </system.web>
</location>