Algo que utilizo bastante a menudo en mis aplicaciones es la gestión de usuarios de .net
Normalmente trabajo con una arquitectura de capas donde la parte web (web Forms o MVC) conecta con un web service o SVC (Capa de negocio) para gestionar datos de la bbdd.
Os dejo este post con los conceptos básicos para montar la gestión de usuarios de .net en nuestras aplicaciones.
1.- Configurar SQL Server
Se ejecuta aspnet_regsql.exe

Accedemos al configurador de SqlServer don de le indicaremos en que servidor y bbdd se va a crear la bbdd de nuestro sistema de usuarios

Terminada la configuración nos en la bbdd que hayamos elegido las tablas que utiliza .net para la gestión de usuarios.

La tabla aspnet_ProfileExtended la veremos mas adelante ya que no se crea de manera automática.
2.- Configurar nuestra aplicación web.
Voy a configurar un servicio en WCF para la gestión de los usuarios. Hay que crear un nuevo proyecto WCF como Aplicación de servicios WCF.
Renombramos la Interface a IUsuarios y el svc a Usuarios.svc

La parte dura de la configuración esta en el web config que se divide en tres partes, Membership, profile, Role
- Membership: Configura la conexión con el proveedor de datos.
Se indica el proveedor de datos que vamos a usar, por defecto el que instalado en el apartado anterior con el asistente de SQL Server.
El resto de parámetros son para configurar los requisitos de usuarios, longitud del password, codificación del pasword, número de intentos de login para que se bloquee el usuario, pregunta de seguridad…
Buscar en google cada parámetro para que sirva aunque son muy intuitivos.
<membership defaultProvider="AspNetSqlMembershipProvider">
<providers>
<clear />
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider"
connectionStringName="UsuariosPortal"
enablePasswordRetrieval="true"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="true"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="6"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="30"
applicationName="/"
passwordFormat="Clear" />
</providers>
</membership>
- Profile: Perfil añadido al proveedor de datos.
Si tenemos datos añadidos a cada usuario, como en este caso es el nombre, apellidos y teléfono y los queremos guardar en una tabla en bbdd.
Si es una tabla de bbdd indicárselo en el parámetro table.
<profile defaultProvider=”AspNetSqlMembershipProvider” enabled=”true”>
<providers>
<clear />
<add name=”SqlTableProfileProvider” type=”System.Web.Profile.SqlTableProfileProvider” connectionStringName=”UsuariosPortal” applicationName=”/” table=”aspnet_ProfileExtended” />
</providers>
<properties>
<add name=”UserID” provider=”SqlTableProfileProvider” type=”string” defaultValue=”fasle” customProviderData=”UserID;nvarchar” />
<add name=”Nombre” provider=”SqlTableProfileProvider” type=”string” defaultValue=”false” customProviderData=”Nombre;nvarchar” />
<add name=”Apellidos” provider=”SqlTableProfileProvider” type=”string” defaultValue=”false” customProviderData=”Apellidos;nvarchar” />
<add name=”Telefono” provider=”SqlTableProfileProvider” type=”string” defaultValue=”false” customProviderData=”Telefono;nvarchar” />
</properties>
</profile>
La tabla aspnet_ProfileExtended hay que crearla en la bbdd manualmente.
- Role: Roles de los usuarios.
Le indicamos donde tenemos el proveedor de roles que es el mismo que de usuarios.
<roleManager enabled="true">
<providers>
<clear />
<add connectionStringName="UsuariosPortal"
applicationName="/"
name="AspNetSqlRoleProvider"
type="System.Web.Security.SqlRoleProvider" />
</providers>
</roleManager>
En la sección de connectionString del web.config añadimos al cadena de conexión a la bbdd donde tenemos el proveedor.
Con esto quedaría configurada nuestra aplicación para conectar con nuestro proveedor de usuario.
3.- Gestionar usuario desde la aplicación.
Hay que enlazar nuestro servicio con la bbdd, para realizar esto lo más sencillo es utilizar “Entity Data Framework”. Añadimos un fichero .edmx con el modelo de datos que vamos a utilizar, que además será, exportable a la capa web y nos ahorrará mucho tiempo y código.

Con el modelo de nuestro perfil de usuario encapsulado en una clase, ya lo tenemos todo para empezar a programar.
4.- Operaciones para gestionar usuarios.
Definimos las operaciones en la Interface.
[ServiceContract]
public interface IUsuarios
{
[OperationContract]
Boolean Login(string UserName, string Password);
[OperationContract]
void OlvidoPassword(string email);
[OperationContract]
bool CrearUsuario(Usuario NuevoUsuario);
[OperationContract]
bool ModificarUsuario(Usuario UsuarioModificador);
[OperationContract]
List<Usuario> ObtenerUsuarios(Usuario UsuarioAdministrador);
[OperationContract]
Usuario ObtenerUsuarioPorUserName(string UserName);
[OperationContract]
Boolean DesbloquearUsuario(string UserName);
[OperationContract]
string[] ObtenerRoles();
[OperationContract]
void ActivacionCaducada(string UserName);
[OperationContract]
bool CambiarPassword(string UserName, string OldPassword, string NewPassword);
}
Y los datos de usuario que queremos que exporte nuestro servicio
[DataContract]
public partial class Usuario
{
[DataMember]
public string email { get; set; }
[DataMember]
public string UserName { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public string Nombre { get; set; }
[DataMember]
public string Apellidos { get; set; }
[DataMember]
public string Telefono { get; set; }
[DataMember]
public string Rol { get; set; }
[DataMember]
public bool Activo { get; set; }
[DataMember]
public bool Bloqueado{ get; set; }
}
Para implementar estas operaciones necesitaremos cuatro clases mas una
- Validaciones.cs: Realiza las validaciones de campos que recibimos.
- Encriptación.cs: La utilizaremos para encriptar datos.
- Mail.cs: Se utilizará para enviar los mails necesarios.
- Usuario.cs: Clase con los métodos de usuario
5.- Clase Usuario
La clase usuario es una clase partitial que se compone del modelo de la bbdd y los métodos. Estos métodos solo son accesibles desde el mismo namespace.
public partial class Usuario
{
internal bool CrearUsuario(Usuario NuevoUsuario, object UserId)
internal bool ModificarUsuario(Usuario UsuarioModificado)
internal List<Usuario> ObtenerUsuarios(Usuario UsuarioAdministrador)
internal Usuario ObtenerUsuario(string UserName)
internal void EnviarActivacion(string email, string UserName)
private string GenerarUrlActivacion(string UserName)
}
- Crear usuario
internal bool CrearUsuario(Usuario NuevoUsuario, object UserId)
{
this.Apellidos = NuevoUsuario.Apellidos;
this.Nombre = NuevoUsuario.Nombre;
this.Entidad = NuevoUsuario.Entidad;
Roles.AddUserToRole(NuevoUsuario.UserName, NuevoUsuario.Rol);
PortalExperianEntities context = new PortalExperianEntities();
aspnet_ProfileExtended profile = new aspnet_ProfileExtended();
profile.Apellidos = Apellidos;
profile.Nombre = Nombre;
profile.UserId = (Guid)UserId;
profile.Telefono = Telefono;
context.AddToaspnet_ProfileExtended(profile);
context.SaveChanges();
EnviarActivacion(NuevoUsuario.email, NuevoUsuario.UserName);
return true;
}
- Modificar Usuario
internal bool ModificarUsuario(Usuario UsuarioModificado)
{
PortalExperianEntities context = new PortalExperianEntities();
MembershipUser user = Membership.GetUser(UsuarioModificado.UserName);
aspnet_ProfileExtended profile = context.aspnet_ProfileExtended.Single(u => u.UserId == (Guid)user.ProviderUserKey);
profile.Apellidos = UsuarioModificado.Apellidos;
profile.Nombre = UsuarioModificado.Nombre;
profile.Telefono = UsuarioModificado.Telefono;
context.SaveChanges();
foreach(string role in Roles.GetRolesForUser(UsuarioModificado.UserName)){
Roles.RemoveUserFromRole(UsuarioModificado.UserName, role);
}
Roles.AddUserToRole(UsuarioModificado.UserName, UsuarioModificado.Rol);
user.IsApproved = UsuarioModificado.Activo;
user.Email = UsuarioModificado.email;
Membership.UpdateUser(user);
return true;
}
- Obtener usuarios
internal List<Usuario> ObtenerUsuarios(Usuario UsuarioAdministrador)
{
List<Usuario> usuarios = new List<Usuario>();
PortalExperianEntities context = new PortalExperianEntities();
var query = from u in context.aspnet_ProfileExtended
select u;
List<aspnet_ProfileExtended> usuarioExtended = query.ToList<aspnet_ProfileExtended>();
foreach (aspnet_ProfileExtended user in usuarioExtended)
{
Usuario usuario = new Usuario();
usuario.Apellidos = user.Apellidos;
usuario.Nombre = user.Nombre;
usuario.Telefono = user.Telefono;
MembershipUser userMembership = Membership.GetUser(user.UserId);
usuario.Rol = Roles.GetRolesForUser(userMembership.UserName)[0].ToString();
usuario.UserName = userMembership.UserName;
if (userMembership.IsLockedOut)
{
usuario.Password = "";
}
else
{
usuario.Password = userMembership.GetPassword();
}
usuario.email = userMembership.Email;
usuario.Bloqueado = userMembership.IsLockedOut;
usuario.Activo = userMembership.IsApproved;
usuarios.Add(usuario);
}
return usuarios;
}
- Obtener Usuario
internal Usuario ObtenerUsuario(string UserName)
{
PortalExperianEntities context = new PortalExperianEntities();
MembershipUser userMembership = Membership.GetUser(UserName);
aspnet_ProfileExtended profile = context.aspnet_ProfileExtended.Single(u => u.UserId == (Guid)userMembership.ProviderUserKey);
Usuario usuario = new Usuario();
usuario.Apellidos = profile.Apellidos;
usuario.Nombre = profile.Nombre;
usuario.Telefono = profile.Telefono;
usuario.Bloqueado = userMembership.IsLockedOut;
usuario.Rol = Roles.GetRolesForUser(userMembership.UserName)[0].ToString();
usuario.UserName = userMembership.UserName;
usuario.Activo = userMembership.IsApproved;
if (userMembership.IsLockedOut)
{
usuario.Password = "";
}
else
{
usuario.Password = userMembership.GetPassword();
}
usuario.email = userMembership.Email;
return usuario;
}
- Enviar Activación
internal void EnviarActivacion(string email, string UserName)
{
mail.MandarMail(email, mail.MailActivacion(UserName, GenerarUrlActivacion(UserName)), "Activacion del servicio");
}
- Generar Activacion
Este es el único método privado de la clase, u lo que hace es encriptar los datos necesarios para activar al usuario.
En el webconfig tenemos que guardar la dirección de la página de activación.
private string GenerarUrlActivacion(string UserName)
{
string url = ConfigurationManager.AppSettings["WebActivacion"].ToString();
url += "?UserName=" + Encriptacion.Encriptar(UserName, Encriptacion.Semilla) + "&fecha=" + Encriptacion.Encriptar(DateTime.Now.ToString("yyyyMMdd"), Encriptacion.Semilla);
return url;
}
Las clases de encriptación, validación y mail son muy comunes y cada uno que las desarrolle como le venga bien.
6.- Código de las Operaciones
Según la lógica de negocio que utilicemos nos harán falta mas operaciones, o el código de cada operación será diferente.
- Login: Permite el acceso a la aplicación.
- OlvidoPassword: Manda un mail para recordar el password al usuario.
- CrearUsuario: Crea un usuario nuevo
- Modificar usuario: modifica los datos de un usuario.
- ObtenerUsuarios: Obtiene todos los usuarios de la bbdd
- ObtenerUsuario: Obtiene el detalle de un usuario.
- ObtenerRoles: Devuelve todos los roles.
- ActivacionCaducada: manda un mail si el link de activación a caducado.
- CambiarPassword: Modifica password.
- Login
public Usuario Login(string UserName, string Password)
{
try
{
//Validas datos
Validaciones.ValidarDatos(UserName, Password);
if (Membership.ValidateUser(UserName, Password))
{
Usuario usuario = new Usuario();
usuario = usuario.ObtenerUsuario(UserName);
if (!usuario.Activo)
{
throw new Exception("Usuario desactivado");
}
return usuario;
}
MembershipUser user = Membership.GetUser(UserName);
if ((user != null) && (user.IsLockedOut))
{
mail.MandarMail(user.Email, mail.MailUsuarioBloqueado(user.UserName), "Usuario Bloqueado");
throw new Exception("Usuario bloqueado");
}
throw new Exception("Usuario o password no es correcto");
}
catch (Exception ex)
{
throw ex;
}
}
- Crear Usuario
El usuario se crea desactivado y se envía un mail con una url para activar.
public bool CrearUsuario(Usuario NuevoUsuario)
{
try
{
Validaciones.ValidarUsuario(NuevoUsuario);
MembershipCreateStatus status;
MembershipUser user = Membership.CreateUser(NuevoUsuario.UserName, NuevoUsuario.Password, NuevoUsuario.email, null, null, false, out status);
if (status == MembershipCreateStatus.Success)
{
try
{
Usuario usuario = new Usuario();
usuario.UserName = NuevoUsuario.UserName;
usuario.Password = NuevoUsuario.Password;
usuario.email = NuevoUsuario.email;
usuario.Telefono = NuevoUsuario.Telefono;
return usuario.CrearUsuario(NuevoUsuario, user.ProviderUserKey);
}
catch (Exception ex)
{
Membership.DeleteUser(NuevoUsuario.UserName);
throw new Exception("No es posible crear usuario " + ex.Message);
}
}
return false;
}
catch (MembershipCreateUserException memberEx)
{
throw memberEx;
}
catch (Exception ex)
{
throw ex;
}
}
- Olvido Password
public void OlvidoPassword(string email)
{
try
{
MembershipUser user = Membership.GetUser(Membership.GetUserNameByEmail(email));
mail.MandarMail(user.Email, mail.MailOlvidoPassword(ObtenerUsuarioPorUserName(user.UserName)), "Recordatorio Portal Experian");
}
catch (Exception ex)
{
throw ex;
}
}
- Modificar usuario
public bool ModificarUsuario(Usuario UsuarioModificado)
{
try
{
Validaciones.ValidarUsuario(UsuarioModificado);
Usuario user = new Usuario();
return user.ModificarUsuario(UsuarioModificado);
}
catch (Exception ex)
{
throw ex;
}
}
- Obtener usuario.
public Usuario ObtenerUsuarioPorUserName(string UserName)
{
try
{
Validaciones.ValidarUserName(UserName);
Usuario usuario = new Usuario();
return usuario.ObtenerUsuario(UserName);
}
catch (Exception ex)
{
throw ex;
}
}
- Obtener usuarios
public List<Usuario>; ObtenerUsuarios(Usuario UsuarioAdministrador)
{
try
{
Validaciones.ValidarUsuario(UsuarioAdministrador);
Usuario usuario = new Usuario();
return usuario.ObtenerUsuarios(UsuarioAdministrador);
}
catch (Exception ex)
{
throw ex;
}
}
- Obtener Roles
public string[] ObtenerRoles()
{
try
{
return Roles.GetAllRoles();
}
catch (Exception ex)
{
throw ex;
}
}
- Desbloquear usuario
public Boolean DesbloquearUsuario(string UserName)
{
try
{
MembershipUser user = Membership.GetUser(UserName);
return user.UnlockUser();
}
catch (Exception ex)
{
throw ex;
}
}
- Activación Caducada
public void ActivacionCaducada(string UserName)
{
Usuario user = new Usuario();
user.EnviarActivacion(Membership.GetUser(UserName).Email, UserName);
}
- Cambiar Password
public bool CambiarPassword(string UserName, string OldPassword, string NewPassword)
{
MembershipUser user = Membership.GetUser(UserName);
return user.ChangePassword(OldPassword, NewPassword);
}
7.- Configuración como web service en la web
Para configurar la web que llama al servicio hay que indicarle en el web config que se va a utilizar como un webservice en el apartado binding con los siguientes parámetros
<wsHttpBinding>
<binding name="WSHttpBinding_IUsuarios" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
El apartado client le indicamos la url y los parámetros del cliente que usuremos en la web
<client>
<endpoint address="http://localhost:1780/Usuarios.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IUsuarios" contract="ServicioUsuarios.IUsuarios"
name="WSHttpBinding_IUsuarios">
</endpoint>
</client>
Para mas profanidad en la configuración de WCF ya hay mucha información en google, sino esto se hace muy extenso.
8.- Web de usuarios
El diagrama que yo he utilizado es

He creado dos roles uno de Administrador y otro de Usuario, los roles los podemos crear por código en el global.asax del servicio o con la aplicación que nos ofrece visual Studio de configuración web.


En el apartado Seguridad podemos gestionar los roles y los usuarios.
No voy a entrar en las llamadas al servicio, se utiliza como cualquier web service ni en el desarrollo de la web.
9.- Conclusiones
Insertar la gestión de usuarios en una aplicación web es algo rápido y sencillo. Espero que os sea util y cualquier consulta podeis escribirme.
Esto es todo.
Que la fuerza os acompañe