Un saludo amantes de la informática, en este post vamos ha hablar sobre el uso de cookies muy relacionado con el inicio de sesiones. En particular las normas sobre desde donde acceder serian las mismas:

  1. No acceder a las cookies desde las vistas, estas deben ser ajenas a la procedencia de los datos que se muestran.
  2. Intentar no acceder desde el modelo, esto ata el modelo a «objetos http» y si por alguna razón no puedes encapsular todo el acceso desde clases «helper» intenta inyectar esas clases con un contenedor IoC.
  3. Acceder desde los controladores, estos ya están atados a todos los objetos http, por eso es uno de los mejores lugares para acceder a las cookies.

Acceso a las cookies de forma manual

Para crear una cookie basta con usar la clase HttpCookie y la coleccion Cookies que esta en Response:

string valueStr = "valor de cookie";
HttpCookie cookie1 = new HttpCookie("cookie_one", valueStr);
ControllerContext.HttpContext.Response.SetCookie(cookie1);

Aquí estamos estableciendo una cookie llamada «cookie_one» con el valor obtenido de la variable «valueStr». La colección «Cookies» que esta en «Response» contiene las cookies que se envían del servidor al cliente.

Para leer una cookie que nos envía el cliente debemos acceder a la coleccion «Cookies», pero debemos usar la que esta en «Request»:

var c1 = ControllerContext.HttpContext.Request.Cookies["cookie_one"];
if (c1 != null)
{
string valor = c1.Value;
}

En el código anterior estamos recuperando «cookie_one» que nos ha enviado el cliente.

Acceso a las cookies mediante «Value Provider»

Bueno… que os parece el siguiente código?

public ActionResult GeCookieManual()
{
var c1 = ControllerContext.HttpContext.Request.Cookies["cookie_one"];
var c2 = ControllerContext.HttpContext.Request.Cookies["
cookie _two"];
FooData fd = null;

if (c1 != null && c2 != null)
{
fd = new FooData()
{
OneValue = c1.Value,
AnotherValue = c2.Value
};
}

return View("Details", fd);
}

Fijaos en lo que estamos haciendo… obtenemos 2 cookies y con ellas estamos creando un objeto de la clase «FooModel».

¿No os suena esto mucho al «binding«? Si los datos estuviesen en la querystring (GET) o bien en (POST), ASP.NET MVC seria capaz de crear automáticamente el objeto «FooModel» y con las cookies no puede?

Pues no. Por lo menos no de serie… pero por suerte ASP.NET MVC es tan extensible que añadir esta característica es muy fácil…

Debemos crearnos un IValueProvider propio, que trabaje con las cookies, (los value providers son los encargados de «parsear» la request del cliente, recoger los datos y dejarlos todos en un mismo sitio para que el model binder pueda realizar el binding). Por defecto hay value providers para querystring y para POST, pero no hay para cookies. Vamos a ver como podríamos crearlo.

La clase que implementa IValueProvider es muy simple:

public class CookieValueProvider : IValueProvider
{
private readonly HttpCookieCollection cookies;

public CookieValueProvider(HttpCookieCollection cookies)
{
this.cookies = cookies;
}

public bool ContainsPrefix(string prefix)
{
return this.cookies[prefix] != null;
}

public ValueProviderResult GetValue(string key)
{
var cookie = this.cookies[key];
return cookie != null ?
new ValueProviderResult(cookie.Value, cookie.Value, CultureInfo.CurrentUICulture) : null;
}
}

Básicamente «ContainsPrefix» devuelve si la clave existe dentro de este value provider y «GetValue» devuelve el valor asociado a la clave. En nuestro caso solo debemos mirar dentro de la colección de cookies que recibimos del constructor.

Ahora toca el controlador, que lo unico que debe hacer es crear un CookieValueProvider y pasarle la coleccion de cookies de la Request:

public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
return new CookieValueProvider(controllerContext.HttpContext.Request.Cookies);
}

Y finalmente, en el Aplication_Start (en Global.asax) incluimos el controlador de value providers:

ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());

Ahora si tenemos la clase FooData definida como:

public class FooData
{
public string OneValue { get; set; }
public string AnotherValue { get; set; }
}

Y si hemos nombrado cookies igual que las propiedades (OneValue, AnotherValue), podemos obtener los valores dejando que actúe el binding de ASP.NET MVC:

public ActionResult GetCookieAuto(FooData data)
{
// data está rellenado con el valor de las cookies
}

Y listos! Nuestro value provider obtiene el valor de las cookies y el model binder se encarga del resto!

Os dejo el link de un post donde vi el value provider para cookies en ASP.NET MVC.

Creación de un inicio de sesión sencillo

Una vez vista la teoría sobre las sesiones vamos a crear un login sencillo en donde podremos poner a prueba lo visto anteriormente.

  • Crearemos en Models una clase llama User, con la siguiente estructura:
public class User
{
    public int Id { get; set; }
    public String UserName { get; set; }
    public String Password { get; set; }
    public String FirstName { get; set; }
    public String LastName { get; set; }

    public List<User> UserList = new List<User>();

    public List<User> ReturnList()
    {
        UserList.Add(new User()
        {
            Id = 1,
            UserName = "Pozu",
            Password = "1234",
            FirstName = "David",
            LastName = "Pozuelo"
        });

        return UserList;
    }
}
  • Crearemos en Controllers un controllador llamado UsersControllers, con la siguiente estructura:
public class UsersController : Controller
{
    // GET: Index
    public ActionResult Index()
    {
        return View();
    }
}
  • Añadimos al controlador el action para el login, tanto GET como POST, si nos damos cuenta en el método POST es donde estamos creando la cookie de la sesión:
    // POST: Login
    [HttpPost]
    public ActionResult Login(String UserName, String Password)
    {
        User user = new User();
        var model = user.ReturnList().Where(z => z.UserName == UserName && z.Password == Password).SingleOrDefault();
        if (model != null)
        {
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, UserName, DateTime.Now, DateTime.Now.AddMinutes(10), true, UserName);
            String Encrypt = FormsAuthentication.Encrypt(ticket);
            HttpCookie cookie = new HttpCookie("TIKECTCOOKIE", Encrypt);

            Response.Cookies.Add(cookie);

            return RedirectToAction("Index", "Users");
        }
        else
        {
            ViewBag.Message = "UserName o Password incorrecto";
        }

        return View();
    }
  • Una vez configurado el action creamos la vista login, en ActionResult Login, boton derecho, crear vista e incluimos el siguiente formulario:
@using (Html.BeginForm())
{
    <div class="card p-4 col-lg-6 m-auto">
        <div class="card-body">
            <div class="form-group">
                <label>Email address</label>
                <input type="text" class="form-control" name="UserName" placeholder="Enter username">                
            </div>
            <div class="form-group">
                <label>Password</label>
                <input type="password" class="form-control" name="Password" placeholder="Password">
            </div>
            <br />
            <input type="submit" class="btn btn-info w-100" value="Login">
        </div>
    </div>
}
  • Ahora nos vamos al fichero Global.asax, para configurar la autenticación de la cookie, debemos añadir el siguiente código:
public void Application_PostAuthenticateRequest()
{
    HttpCookie cookie = Request.Cookies["TIKECTCOOKIE"];            
            
    AuthenticationCookie(cookie);
}

public void AuthenticationCookie(HttpCookie cookie)
{
    if (cookie != null)
    {
        String data = cookie.Value;
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(data);

        String username = ticket.Name;
        String role = ticket.UserData;

        GenericIdentity identity = new GenericIdentity(username);
        GenericPrincipal user = new GenericPrincipal(identity, new String[] { role });

        HttpContext.Current.User = user;
    }
}
  • Ahora debemos probar lo que estamos haciendo y para que esto tenga sentido debemos crear una vista personal para cuando un usuario inicie sesión, nosotros vamos a usar la vista Index de UsersControllers.
@if (HttpContext.Current.User.Identity.IsAuthenticated)
{
<p>Bienvenido a tu sesion @HttpContext.Current.User.Identity.Name !!!</p>
}
else 
{
    <p>Todavia no has iniciado sesion</p>
}
  • Hasta aquí todo funciona pero si no podemos cerrar la sesión no tiene sentido el uso de sesiones, así que vamos a crear un método para cerrar la sesión, lo incluiremos en UserControllers:
        // GET: Logout
        public ActionResult Logout()
        {
            HttpContext.User = new GenericPrincipal(new GenericIdentity(""), null);
            FormsAuthentication.SignOut();
            HttpCookie cookie = Request.Cookies["TIKECTCOOKIE"];
            cookie.Expires = DateTime.Now.AddYears(-1);
            Response.Cookies.Add(cookie);

            return RedirectToAction("Login", "Users");
        }
  • Y ya tendríamos nuestro inicio de sesión, a sido fácil verdad, espero que os lo hayáis pasado bien y ya sepáis mas sobre las cookies.

El código del proyecto lo dejare subido a mi GitHub para que los que queráis lo descargáis y lo probéis.

Descargar proyecto

También podéis encontrarme en LinkedIn

Un saludo a todos!!!!

Autor/a: David Pozuelo Martinez
Curso: Microsoft MCSA Web Applications + Microsoft MCSD App Builder + Xamarin
Centro: Tajamar
Año académico: 2018-2019

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.