9 de febrero de 2015

Autenticación y autorización basada en formularios ASP.NET

Para poder activar la seguridad basada en formularios hemos de seguir los siguientes pasos:

· Configurar el modo de autenticación modificando la sección en web.config.
· Denegar a los usuarios anónimos en la sección 
· Crear una página de registro.

Se utilizara la siguiente tabla para validar las credenciales de un usuario, esta tabla además de guardar el username y el password tiene un campo donde se definen los roles del usuario, tener en cuenta que en caso del usuario que tenga mas de un rol, estos roles deben estar unidos y solamente separados por una coma sin espacio


Ahora toca crear un nuevo proyecto, para este ejemplo se utilizara una aplicación ASP.Net de Web Forms


Se agregara un nuevo ítem de tipo Web Form


Ahora se debe dar click derecho en el proyecto, seleccionar propiedades del proyecto y en el panel que se abre seleccionar la opcion Web que se encuentra en la izquierda de la pantalla para luego modificar como se muestra(cambiar el IIS Express por Local IIS) a fin de hospedar el proyecto web en el servidor local, luego recordar presionar el botón Create Virtual Directory



Abrir el Inetmgr y buscar en el panel central el icono Authentication y modificar tal como se muestra a fin de habilitar el modo de autenticacion de formularios en el servidor local de IIS



Ahora toca modificar el web.Config



·         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.
Además, en algunos sitios web de negocios, varios empleados tendrían acceso a un sistema con el fin de realizar tareas específicas. Sin embargo, cada empleado tendría un papel específico, y las operaciones específicas que hacer, de acuerdo con la naturaleza de su trabajo o nivel de seguridad.
ASP.NET proporciona el concepto de los roles que le da a cada rol una visión diferente en páginas específicas.

<location path="adminRol">
      <system.web>
        <authorization>
          <allow roles="admon" />
          <deny users="*" />
        </authorization>
      </system.web>
    </location>
 
  <location path="Usuario">
      <system.web>
        <authorization>
          <allow roles="user" />
          <deny users="*" />
        </authorization>
      </system.web>
    </location>
 
  <location path="managerRol">
    <system.web>
      <authorization>
        <allow users="lebb"/>
       
        <deny users="*" />
      </authorization>
    </system.web>
  </location>

·         Validar Credenciales contra la BD
Para realizar la conexión a la base de datos, se utilizara el siguiente código que verifica si un usuario está debidamente registrado, importar estos espacios de nombre

using System.Data.SqlClient;
using System.Web.Security;
private bool ValidateUser( string userName, string passWord ){
       SqlConnection conn;
       SqlCommand cmd;
       string lookupPassword = null;

       // Buscar nombre de usuario no válido.
       // el nombre de usuario no debe ser un valor nulo y debe tener entre 1 y 15 caracteres.
       if ((null == userName) || (0 == userName.Length) || (userName.Length > 15)){
             System.Diagnostics.Trace.WriteLine( "[ValidateUser] userName failed." );
             return false;
       }

       // Buscar contraseña no válida.
       // La contraseña no debe ser un valor nulo y debe tener entre 1 y 25 caracteres.
       if ((null == passWord)||( 0 == passWord.Length )||(passWord.Length > 25)){
             System.Diagnostics.Trace.WriteLine( "[ValidateUser] passWord failed." );
             return false;
       }

       try{
             conn = new SqlConnection( "server=localhost;Integrated Security=SSPI;database=pubs" );
       conn.Open();
       cmd = new SqlCommand( "Select pwd from users where uname=@userName", conn );
       cmd.Parameters.Add( "@userName", SqlDbType.VarChar, 25 );
       cmd.Parameters["@userName"].Value = userName;

             lookupPassword = (string) cmd.ExecuteScalar();
             cmd.Dispose();
             conn.Dispose();
       }
       catch ( Exception ex ){
       // Agregar aquí un control de errores para la depuración.
       // Este mensaje de error no debería reenviarse al que realiza la llamada.
       System.Diagnostics.Trace.WriteLine( "[ValidateUser] Exception " + ex.Message );
       }

       // Si no se encuentra la contraseña, devuelve false.
       if ( null == lookupPassword ){
             // Para más seguridad, puede escribir aquí los intentos de inicio de sesión con error para el registro de eventos.
             return false;
       }

       // Comparar lookupPassword e introduzca passWord, usando una comparación que distinga mayúsculas y minúsculas.
       return ( 0 == string.Compare( lookupPassword, passWord, false ) );
}


·         Cookie de Autenticación
Puede usar dos métodos para generar la cookie de autenticación de formularios y redirigir al usuario a la página apropiada del evento cmdLogin_ServerClick. Se proporciona código de ejemplo para ambas situaciones. Utilice cualquiera de ellos de acuerdo a sus requisitos.

1.       Llame al método RedirectFromLoginPage para generar automáticamente la cookie de autenticación de formularios y redirigir al usuario a una página apropiada del evento cmdLogin_ServerClick:

private void cmdLogin_ServerClick(object sender, System.EventArgs e){
if (ValidateUser(txtUserName.Value,txtUserPass.Value))
       FormsAuthentication.RedirectFromLoginPage(txtUserName.Value,
             chkPersistCookie.Checked);
       else
             Response.Redirect("logon.aspx", true);
}
// Cerrar la sesión
FormsAuthentication.SignOut();                                                                      

También podría ayudar este código

private void cmdLogin_ServerClick(object sender, System.EventArgs e){
if (ValidateUser(txtUserName.Value,txtUserPass.Value)){

  FormsAuthentication.SetAuthCookie(txtUsuario.Text, false);
     Response.Redirect("OtraPagina.aspx");
    else
     Response.Redirect("logon.aspx", true);
}}
// Cerrar la sesión
FormsAuthentication.SignOut();

2.       Genere el vale de autenticación, cífrelo, cree una cookie, agréguela a la respuesta y redirija al usuario. Esto le proporcionará un mayor control sobre cómo ha creado la cookie. También puede incluir datos personalizados junto con FormsAuthenticationTicket en este caso.

protected void BLogin_Click(object sender, EventArgs e){
            FormsAuthentication.Initialize();

string cnx = ConfigurationManager.ConnectionStrings["cnx"].ConnectionString.ToString();
if ((!string.IsNullOrEmpty(txtUsername.Text)) && (!string.IsNullOrEmpty(txtPassword.Text)))
            {
                string usuario = txtUsername.Text.Trim();
                string password = txtPassword.Text.Trim();

  using (SqlConnection cnn = new SqlConnection(cnx)){

         SqlCommand cmd = cnn.CreateCommand();
         cmd.CommandText = "SELECT Roles FROM Login_ASP WHERE Username=@username " +
                     "AND Password=@password";

         cmd.Parameters.Add("@username", SqlDbType.VarChar, 50).Value = usuario;
         cmd.Parameters.Add("@password", SqlDbType.VarChar, 50).Value = password;

                    cnn.Open();
                    SqlDataReader reader = cmd.ExecuteReader();
                    if (reader.Read()){
                       
                       
     FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                           1, // Ticket version
                           usuario, // Username asociado al ticket
                           DateTime.Now, // Date/time de creacion
                           DateTime.Now.AddMinutes(30), // Date/time de expiracion
                           true, // "true" para persistencia de cookie
                           reader.GetString(0), // User-data, en este caso los roles
                           FormsAuthentication.FormsCookiePath);// Path de la cookie

                        // Encriptacion de la cookie
                        string hash = FormsAuthentication.Encrypt(ticket);
                        // Nombre y valor del Hashed ticket
      HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash);
                        
                   // definir la cookie's expiration time al tickets expiration time
                        if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;

                        // Agregar la cookie a la lista de outgoing response
                        Response.Cookies.Add(cookie);

      // Redireccionar a la URL solicitada o homepage if no previous page requested
                        string returnUrl = Request.QueryString["ReturnUrl"];
                        if (returnUrl == null) returnUrl = "/";

                   // Don't call FormsAuthentication.RedirectFromLoginPage since it
                   // could replace the authentication ticket (cookie) we just added
                        Response.Redirect(returnUrl);
                        //string rol = reader.GetString(0).ToString();
                        //Label1.Text = rol;
                        reader.Close();

                    }
                    else{
              // Never tell the user if just the username is password is incorrect.
              // That just gives them a place to start, once they've found one or
             // the other is correct!
                  Label1.Text = "Username / password incorrect. Please try again.";
                      
                    }}}}

Antes de continuar, unos tips básicos al utilizar cookies de los HttpModules

·         Redireccionamiento Al redireccionar al usuario no usar el RedirectFromLoginPage para que se puedan usar las Cookies de los HttpModules
Response.Redirect( FormsAuthentication.GetRedirectUrl(user,false));
·         Verificar usuario autenticado HttpContext .Current.User.Identity.IsAuthenticated
·         Obtener el nombre del usuario HttpContext .Current.User.Identity.Name
·         Comprobar rol del usuario HttpContext .Current.User.IsInRole( "admin" ) 

Ahora en el archivo Global.asax
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
        {
 if (HttpContext.Current.User != null) //Hay un usuario?
            {
 if (HttpContext.Current.User.Identity.IsAuthenticated) //El usuario esta autenticado?
                {
 if (HttpContext.Current.User.Identity is FormsIdentity) //utilizo Forms?
 //if (HttpContext.Current.User.Identity.AuthenticationType == "Forms") equivalente al anterior
                    {
 FormsIdentity id =                           (FormsIdentity)HttpContext.Current.User.Identity; //guardo el actual usuario en id
FormsAuthenticationTicket ticket = id.Ticket; //tomo toda la informacion de id en ticket

                        // Get the stored user-data, in this case, our roles
                        string userData = ticket.UserData;
                        string[] roles = userData.Split(',');
                        HttpContext.Current.User = new GenericPrincipal(id, roles);
                    }
                }
            }
        }

Ahora, como pueden ver en la sección Locations, definí tres páginas, les muestro el código de cada página de acuerdo a las restricciones definidas anteriormente

managerRols.aspx
public partial class managerRol : System.Web.UI.Page{
        protected void Page_Load(object sender, EventArgs e){
            string usuario = HttpContext.Current.User.Identity.Name;
            Label1.Text = usuario;
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            FormsAuthentication.SignOut();
            Response.Redirect("Default.aspx");
        }
    }

adminRol.aspx
public partial class adminRol : System.Web.UI.Page{
        protected void Page_Load(object sender, EventArgs e){
            if (HttpContext.Current.User.Identity.IsAuthenticated){
             string usuario = HttpContext.Current.User.Identity.Name;
             Label1.Text = usuario;
           }}

        protected void Button1_Click(object sender, EventArgs e){
            FormsAuthentication.SignOut();
            Response.Redirect("Default.aspx");
        }
    }

Ususario.aspx
public partial class Usuario : System.Web.UI.Page{
        protected void Page_Load(object sender, EventArgs e){
            if (User.IsInRole("user")) {
                Label1.Text = "Bienvenido Usuario";
            }
        }
    }