jueves, 31 de diciembre de 2009

ASP.NET - Eventos de Controles de Usuario (ASCX)

El código completo del ejemplo lo puedes encontrar aquí.

A la hora de crear controles de usuario solemos necesitar capturar un evento que se produce dentro del mismo, en la página padre. Además, normalmente, necesitaremos acceder a alguna de sus propiedades desde la página padre. En el ejemplo que os presento tenemos un control de usuario (un buscador) incrustado en una página que mostrará los resultados de la búsqueda.

Para ello el control de usuario posee un evento que se lanza una vez la búsqueda ha finalizado, además, los resultados de dicha búsqueda se almacenan en una propiedad del control de usuario que recupera la página para mostrarlos en un GridView.

Pasemos al código. El control de usuario, en su parte visual se compone únicamente de un TextBox (donde introducir el criterio de búsqueda) y un Button (que realiza la búsqueda).


 



 

La generación del evento se realiza desde el code-behind:


 

#region Eventos del control

public event
EventHandler AceptarClicked;

protected
virtual
void OnClick(object sender)

{

if (this.AceptarClicked != null)

{

this.AceptarClicked(sender, new EventArgs());

}

}

#endregion


 

Este código declara el evento (cuyo nombre será AceptarClicked) e implementa el método que invoca el evento.


 

#region Propiedades publicas

private
List<string> resultados;

public List<string> Resultados

{

get { return resultados; }

set { resultados = value; }

}

#endregion


 

El código anterior declara una propiedad pública dentro del control donde se almacenarán los resultados de la búsqueda.

En el evento del botón del control de usuario realizamos la llamada al evento público que será capturado por la página:


 

protected
void btnBuscar_Click(object sender, EventArgs e)

{

//Hardcode para simular una búsqueda

this.resultados = new
List<string>();

if (String.IsNullOrEmpty(this.txtPalabraClave.Text))

{

this.resultados.Add("No se han encontrado resultados");

}

else

{

this.resultados.Add("Resultado1 para " + this.txtPalabraClave.Text);

this.resultados.Add("Resultado2 para " + this.txtPalabraClave.Text);

this.resultados.Add("Resultado3 para " + this.txtPalabraClave.Text);

this.resultados.Add("Resultado4 para " + this.txtPalabraClave.Text);

}

//Se invoca el evento público una vez la función del control ha terminado

OnClick(sender);

}


 

La página debe capturar el evento, esto se hace de igual forma que se captura el evento de un botón o cualquier control de .NET. Así, en la presentación de la página tendremos:


 

<uc1:Buscador ID="Buscador1"
runat="server" OnAceptarClicked="Buscador1_AceptarClicked"/>


 

Así el método de tratar el evento del control de usuario, dentro de la página padre será Buscador1_AceptarClicked y se codificará así:


 

protected
void Buscador1_AceptarClicked(object sender, EventArgs e)

{

if (this.Buscador1.Resultados != null)

{

this.GridView1.DataSource = this.Buscador1.Resultados;

this.GridView1.DataBind();

}

}


 

El código completo del ejemplo lo puedes encontrar aquí.

Y esto es todo, espero que te sirva

ASCX - Creación de Controles de Usuario

Una buena práctica, y en mi opinión necesaria para cualquier desarrollo Web, es plantearse qué secciones de las páginas de la aplicación se mostrarán en varias páginas. Estas secciones serán susceptibles de desarrollarse como controles de usuario (User Controls .NET).

Así, podríamos definir (a alto nivel) un control de usuario como una sección de una página que se repite en varias páginas. El hecho de crear un control de usuario nos evitará hacer Copy-Paste (tanto de la parte de presentación como del codebehind del control) de esta sección a lo largo de toda nuestra aplicación, además, a la hora de cambiar algo dentro de esta sección de la página, esta se encuentra en un solo punto y no en varias páginas de nuestra solución.

Para añadir un control de usuario a nuestra sitio Web, botón derecho sobre nuestro proyecto -> Agregar Nuevo Elemento > Control de Usuario Web.


 

En función del número de controles de usuario que preveamos que vamos a precisar convendrá pensar si debemos distribuirlos en carpetas dentro de nuestra solución. Yo normalmente creo una carpeta de "Controles" dentro de mi solución, donde voy almacenando mis controles de usuario.

Bien, ya tenemos nuestro control de usuario en nuestra solución, aquí puedes encontrar el ejemplo de una aplicación en que utilizo un control de usuario y será en esta solución en la que me base de aquí en adelante (en esta solución no he creado ninguna carpeta de controles ya que sólo tengo un control). Este control se corresponde con el menú de mi aplicación y, por tanto, se encontrará en todas las páginas y por ello está incluido en la página master de la solución. El código de mi ascx es el siguiente:

 
 

<%@ Control Language="C#" AutoEventWireup="true"
CodeBehind="LoginControl.ascx.cs" Inherits="AutenticacionFormulario.LoginControl" %>

<asp:HyperLink ID="HyperLink1" runat="server" Text="Inicio" NavigateUrl="~/Default.aspx" /> |

<asp:HyperLink ID="HyperLink2"
runat="server"
Text="Administracion"
NavigateUrl="~/Admin/Admin.aspx" /> |

<asp:HyperLink ID="HyperLink3"
runat="server"
Text="Usuario"
NavigateUrl="~/User/User.aspx"
/> |

<asp:HyperLink ID="hyp1" runat="server" Text="Pagina segura" NavigateUrl="~/Secure/Administracion.aspx"/> |

<asp:LoginView ID="lv1"
runat="server">

<AnonymousTemplate>

Bienvenido, invitado

</AnonymousTemplate>

<LoggedInTemplate>

Bienvenido <asp:LoginName
runat="server"
ID="ln1"
ForeColor="Green"
Font-Bold="true"
/>,

</LoggedInTemplate>

</asp:LoginView>

<asp:LoginStatus
runat="server"
id="ls1"
/>

 
 

Cómo puedes ver un Control de Usuario se codifica exactamente igual que una página ASPX.

Para utilizar un Control de Usuario en una de tus páginas únicamente tendrás que arrastrarlo desde el "Explorador de Soluciones" hasta la página (esta debe estar abierta y en modo diseño). Esta acción generará lo siguiente en la página donde incluyes el control de usuario:

 
 

- Registro del control de usuario dentro de la página:

 
 

<%@ Register
src="LoginControl.ascx"
tagname="LoginControl"
tagprefix="uc1"
%>

 
 

- Inclusión del control de usuario en la página:

 
 

<uc1:LoginControl
ID="LoginControl1"
runat="server"
/>

 
 

Esto es todo no olvides pensar en crear controles de usuario, te facilitarán tanto el desarrollo como el mantenimiento de tus aplicaciones.

CSS - Cambiar la imagen del cursor

Esto no tiene que ver con .NET pero me ha surgido la duda de cómo hacerlo mientras navegaba por la web.

Aunque en mi opinión el ratón es la herramienta fundamental del internauta y por tanto no conviene cambiar su imagen, el cambio de la imagen del cursor puede causar en el usuario una impresión de novedad.

Es muy sencillo, sólo tenemos que añadir el siguiente código a nuestra página o a nuestra hoja de estilos CSS:


 

<style
type="text/css">

body, a, a:hover

{cursor: url(http://cur.cursors-4u.net/anime/ani-10/ani973.ani),
progress;}

</style>


 

En esta página puedes encontrar muchos cursores.

miércoles, 30 de diciembre de 2009

ASP.NET Autenticación por Formulario (Parte II)

Puedes descargarte el ejemplo completo aquí.

En este post voy a explicar cómo manejar los roles de los usuarios con una autenticación basada en formularios.

En mi anterior post ASP.NET Autenticación por Formulario (Parte I) explico cómo crear una aplicación cuya seguridad está basada en la autenticación por formulario, con una sección de administración. El paso siguiente dentro de la seguridad de un sitio Web es aplicar significado a los roles de usuario, para ello vamos a crear diferentes regiones seguras dentro de la aplicación en las que el usuario tendrá acceso en función de sus roles asignados. En esta ocasión la autenticación se realiza contra una base de datos, a la cual accedemos mediante queries LINQ (ya explicaré cómo utilizar LINQ para el acceso a datos en un próximo post).

Voy a partir de la solución de mi anterior post que puedes descargar aquí.

He seguido lo siguientes pasos:

1.- He añadido una base de datos a mi solución una base de datos:

- Botón derecho sobre el proyecto - >Agregar > Nuevo elemento.

- Se selecciona "Base de Datos SQL Server".



2.- He creado las siguientes tablas:

- USUARIO: Contiene todos los usuarios registrados en nuestro sistema. Tiene los siguientes campos: ID_USUARIO, LOGIN, PASSWORD

- ROLE: Que contendrá todos los roles que necesita nuestra aplicación. Tiene los campos: ID_ROLE, DESCRIPCION.

- ROLES_USUARIO: Mantiene la relación n:m entre los usuarios y sus roles, permitiendo que un usuario pueda tener varios roles.

En el ejemplo que os presento existen dos roles (admin y user) y dos usuarios, así el usuario administrador tendrá todos los roles para poder acceder a todas las secciones de la aplicación.

 
 

3.- He agregado un Diagrama de Clases LINQ to SQL.

- Botón derecho sobre el proyecto -> Agregar > Nuevo elemento.

- Se selecciona "Clases de LINQ to SQL".


 

- Se rellena diagrama de clases LINQ (simplemente arrastrando la tabla desde el "Explorador de Servidores" hasta el diagrama de clases. Además he añadido las asociaciones entre las tablas obteniendo el resultado siguiente:


Se generarán así una serie de clases que nos permiten el acceso a la base de datos de una forma transparente, además de auto configurarse un ConnectionString.

4.- Creación de clases ADO para el acceso a los datos:

- He creado dos clases, Rol y Usuario:

La clase Rol está implementada así:

public class
Rol

{

AutenticacionDataClassesDataContext dc = new AutenticacionDataClassesDataContext();

const
char SI = 'S';

internal void AddRole (string description)

{

ROLE role = new
ROLE();

role.DESCRIPCION = description;

dc.ROLE.InsertOnSubmit(role);

dc.SubmitChanges();

}

internal
ROLE FindRol(Guid? codigoRole)

{

var roles = from u in dc.ROLE

where u.ID_ROLE == codigoRole

select u;

if (roles.Count() > 0)

return roles.First();

else

return null;

}

}

 
 

La clase Usuario tiene el siguiente código:

 
 

public
class
Usuario

{

AutenticacionDataClassesDataContext dc = new AutenticacionDataClassesDataContext();

const char SI = 'S';

internal void AddUsuario(string login, string password)

{

USUARIO usuario = new USUARIO();

usuario.LOGIN = login;

usuario.PASSWORD = password;

dc.USUARIO.InsertOnSubmit(usuario);

dc.SubmitChanges();

}

 internal USUARIO FindUsuario(Guid codigoUsuario)

{

var usu = from u in dc.USUARIO

where u.ID_USUARIO == codigoUsuario

select u;

if (usu.Count() > 0)

return usu.First();

else

return null;

}

internal
USUARIO FindUsuario(string login, string password)

{

var usu = from u in dc.USUARIO

where (u.LOGIN == login && u.PASSWORD == password)

select u;

if (usu.Count() > 0)

return usu.First();

else

return null;

}

internal void AddRole(Guid idUsuario, Guid idRole)

{

ROLES_USUARIO rolesUsuario = new ROLES_USUARIO();

rolesUsuario.ID_ROLE = idRole;

rolesUsuario.ID_USUARIO = idUsuario;

dc.ROLES_USUARIO.InsertOnSubmit(rolesUsuario);

dc.SubmitChanges();

}

internal List<ROLE> FindRolesUsuario(Guid? codigoUsuario)

{

List<ROLE> listRole = new
List<ROLE>();

var roles = from u in dc.ROLES_USUARIO

where u.ID_USUARIO == codigoUsuario

select u;

if (roles.Count() > 0)

{

IEnumerable<ROLES_USUARIO> rolesUsuario = roles.AsEnumerable<ROLES_USUARIO>();

foreach (ROLES_USUARIO roleUusario in rolesUsuario)

{

Rol rolADO = new
Rol();

ROLE rol = rolADO.FindRol(roleUusario.ID_ROLE);

if (rol != null)

{

listRole.Add(rol);

}

}

}

return listRole;

}

}

 
 

He implementado algunos métodos que no necesitaré en esta versión, no prestéis especial atención a como están implementados, no creo que sirvan de referencia como uso de LINQ :D.

 
 

5.- Implementación de la autenticación de usuarios:

 
 

Bien, una vez que tenemos listo nuestras clases de acceso a datos con los métodos que vamos a precisar para recuperar los usuarios y sus roles, podemos pasar a implementar la autenticación de los usuarios. He implementado el evento de autenticación de la siguiente forma:

 
 

protected
void Login1_Authenticate(object sender, AuthenticateEventArgs e)

{

string userName = Login1.UserName;

string password = Login1.Password;

bool rememberUserName = Login1.RememberMeSet;

Usuario usuarioADO = new Usuario();

USUARIO usuario = usuarioADO.FindUsuario(userName, password);

if (usuario != null)

{

List<ROLE> roles = usuarioADO.FindRolesUsuario(usuario.ID_USUARIO);

if (roles != null)

{

string rolesData = "";

foreach (ROLE role in roles)

{

rolesData += role.DESCRIPCION.ToString() + ";";

}

// Se crea el ticket de autenticación

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(

1, // Versión del ticket

userName,// Nombre de usuario asociado al ticket

DateTime.Now, // Fecha de creación del ticket

DateTime.Now.AddMinutes(50), // Fecha y Hora de las expiración de la cookie

rememberUserName, // Si el usuario cliquó en "Recuérdame" la cookie no expira.

rolesData, // Almacena datos del usuario, en este caso los roles

FormsAuthentication.FormsCookiePath); // El path de la cookie especificado en el Web.Config

// Se encripta la cookie para añadir más seguridad

string hashCookies = FormsAuthentication.Encrypt(ticket);

// Cookie encriptada

HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashCookies);

// Se añade la cookie a la respuesta

Response.Cookies.Add(cookie);

// Se recupera la url que el usuario trataba de acceder

string returnUrl = Request.QueryString["ReturnUrl"];

// si no existe dicha url redirigimos al usuario a la pagina por defecto

if (returnUrl == null) returnUrl = "~/Default.aspx";

Response.Redirect(returnUrl);

}

}

else // usuario y password erróneos

{

// no se hace nada, el control Login mostrará automáticament los errores

}

}

 
 

Este código autentica a los usuarios contra nuestra base de datos y añade los roles del usuario.

 
 

6.- Creamos los directorios seguros en función de los roles:

Creamos los siguientes directorios:

 
 

- Admin: Destinado únicamente para usuarios administradores (usuarios con el rol 'admin').

- User: Destinado a los usuarios del sitio (con el rol 'user'). P.e: Este directorio podría contener las páginas donde el usuario gestiona su propia cuenta

Se securizan estos directorios modificando el Web.config de la siguiente forma:

 
 

<location path="Admin">

<system.web>

<authorization>

<allow roles="admin"/>

<deny users="*" />

</authorization>

</system.web>

</location>

<location path="User">

<system.web>

<authorization>

<allow roles="user"/>

<deny users="*"/>

</authorization>

</system.web>

</location>

 
 

7.- Creamos control de usuario:

Este control de usuario se añade para poder hacer pruebas de nuestra autenticación y se añade a la página master de nuestra aplicación (Ah! Sí también creé una página master para que este control de usuario esté disponible en todas las páginas de nuestro sitio).


8.- Modificamos el fichero Global.asax (si no existiese lo agregaríamos)

En este fichero se añade la gestión de la autenticación de un usuario, en el siguiente código:

 
 

protected void Application_AuthenticateRequest(object sender, EventArgs e)

{

// Si se envía alguna información acerca del usuario

if (HttpContext.Current.User != null)

{

// se comprueba que el usuario esté autenticado

if (HttpContext.Current.User.Identity.IsAuthenticated)

{

// Se comprueba si el usuario está autenticado por formulario

if (HttpContext.Current.User.Identity is FormsIdentity)

{

// Se recupera la identidad del usuario para recuperar sus roles

FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;

// Se recupera el ticket del usuario

FormsAuthenticationTicket ticket = identity.Ticket;

// Se recupera la información acerca del usuario, donde metimos la información de los roles

string[] roles = ticket.UserData.Split(';');

// Se crea un usuario con dichos roles

HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(identity, roles);

}

}

}

}

 
 

¡Y esto es todo!

Puedes descargarte el ejemplo completo aquí.

lunes, 28 de diciembre de 2009

ASP.NET Autenticación por Formulario (Parte I)

Puedes descargar el código aquí.

En este post voy a explicar cómo crear una aplicación Web en la que existirá una sección segura. La autenticación vía formulario se suele utilizar para securizar el acceso a una determinada página (o grupo de páginas) a usuario no autenticados.

Las páginas a las que deseas restringir el acceso pueden ser las páginas de administración de tu sitio Web. Sigue los pasos siguientes:


Paso 1 - Creación de página de login:

- Crea un nuevo proyecto desde Visual Studio (Sitio Web o Aplicación Web)

- Añade una página "Login.aspx".

- Agrega un control asp:login a la página "Login.aspx".




- Implementación del evento Authenticate del control de Login.


protected
void Login1_Authenticate(object sender, AuthenticateEventArgs e)

{

bool authenticated = AuthenticateMe(Login1.UserName, Login1.Password, Login1.RememberMeSet);


if (authenticated)

{


FormsAuthentication.RedirectFromLoginPage(Login1.UserName, Login1.RememberMeSet);

}

}

private
bool AuthenticateMe(string userName, string password, bool rememberUserName)

{

//Se trata de un hardcode, en un caso real la validación se hará contra una base de datos

string localUserName = "admin";

string localPassword = "1234";

if (userName.Equals(localUserName) && password.Equals(localPassword))

{return true;}

else{return false;}

}


Paso 2 - Modificación del Web.config:

- En este paso se modifica el Web.config para indicar la url por defecto del sitio (defaultUrl), la url de la página de login (loginUrl), se indica si la sesión segura espira (slidingExpiration) y el timeout en minutos.


<system.web>

<authentication
mode="Forms">

<forms
defaultUrl="default.aspx"
loginUrl="~/login.aspx"
slidingExpiration="true"
timeout="20"></forms>

</authentication>

<authorization>

</authorization>

</system.web>


Paso 3 - Creación de un directorio seguro:

Dentro de este directorio se encontrarán aquellas páginas cuyo acceso se quiere restringir.

- Crea una nueva carpeta dentro de tu proyecto (p.e: Secure).

- Dentro de este directorio añade una página (p.e: Administracion.aspx).

- Añade un Web.config dentro de esta carpeta e incluye el siguiente contenido en el mismo.


<?xml version="1.0" encoding="utf-8"?>

<configuration>

<appSettings/>

<connectionStrings/>

<system.web>

<authorization>

<deny users="?"/>

</authorization>

</system.web>

</configuration>


Paso 4 - Ejecuta tu aplicación:

Selecciona la página Administracion.aspx como página de inicio y pulsa F5. Serás redireccionado a la página de Login, una vez hayas introducido las credenciales correctas, la página de login re redireccionará hasta la página de Administracion.aspx

Puedes descargar el código aquí.