2010-09-28 31 views
5

estoy tratando de utilizar una costumbre proveedor de ITempDataProvider para almacenar TempData en una cookie del navegador en lugar de estado de sesión. Sin embargo, todo funciona bien, excepto que no puedo eliminar la cookie de la secuencia de respuesta después de leerla.ASP.NET MVC TempData en cookie del navegador

¿Alguna idea?
Gracias!

public class CookieTempDataProvider : ITempDataProvider 
    { 
     internal const string TempDataCookieKey = "__ControllerTempData"; 
     HttpContextBase _httpContext; 

     public CookieTempDataProvider(HttpContextBase httpContext) 
     { 
      if (httpContext == null) 
      { 
       throw new ArgumentNullException("httpContext"); 
      } 
      _httpContext = httpContext; 
     } 

     public HttpContextBase HttpContext 
     { 
      get 
      { 
       return _httpContext; 
      } 
     } 

     protected virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) 
     { 
      HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey]; 
      if (cookie != null && !string.IsNullOrEmpty(cookie.Value)) 
      { 
       IDictionary<string, object> deserializedTempData = DeserializeTempData(cookie.Value); 

       // Remove cookie     
       cookie.Expires = DateTime.MinValue; 
       cookie.Value = string.Empty; 
       _httpContext.Request.Cookies.Remove(TempDataCookieKey); 

       if (_httpContext.Response != null && _httpContext.Response.Cookies != null) 
       { 
        HttpCookie responseCookie = _httpContext.Response.Cookies[TempDataCookieKey]; 
        if (responseCookie != null) 
        { 
         // Remove cookie 
         cookie.Expires = DateTime.MinValue; 
         cookie.Value = string.Empty; 
         _httpContext.Response.Cookies.Remove(TempDataCookieKey); 

        } 
       } 

       return deserializedTempData; 
      } 

      return new Dictionary<string, object>(); 
     } 

     protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 
     { 

      string cookieValue = SerializeToBase64EncodedString(values); 
      var cookie = new HttpCookie(TempDataCookieKey); 
      cookie.HttpOnly = true; 
      cookie.Value = cookieValue; 

      _httpContext.Response.Cookies.Add(cookie); 
     } 

     public static IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData) 
     { 
      byte[] bytes = Convert.FromBase64String(base64EncodedSerializedTempData); 
      var memStream = new MemoryStream(bytes); 
      var binFormatter = new BinaryFormatter(); 
      return binFormatter.Deserialize(memStream, null) as IDictionary<string, object> /*TempDataDictionary : This returns NULL*/; 
     } 

     public static string SerializeToBase64EncodedString(IDictionary<string, object> values) 
     { 
      MemoryStream memStream = new MemoryStream(); 
      memStream.Seek(0, SeekOrigin.Begin); 
      var binFormatter = new BinaryFormatter(); 
      binFormatter.Serialize(memStream, values); 
      memStream.Seek(0, SeekOrigin.Begin); 
      byte[] bytes = memStream.ToArray(); 
      return Convert.ToBase64String(bytes); 
     } 

     IDictionary<string, object> ITempDataProvider.LoadTempData(ControllerContext controllerContext) 
     { 
      return LoadTempData(controllerContext); 
     } 

     void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 
     { 
      SaveTempData(controllerContext, values); 
     } 
    } 
+2

Tenga mucho cuidado con lo que almacena en la cookie del cliente. En general, es una mala idea mantener las cosas allí. No estoy tratando de juzgar, pero está haciendo este tipo de cosas que llevaron a que [DotNetNuke sufriera mal] (http://www.youtube.com/watch?v=yghiC_U2RaM) del reciente [oráculo relleno exploit] (http: // weblogs.asp.net/scottgu/archive/2010/09/18/important-asp-net-security-vulnerability.aspx). –

+0

De acuerdo con @cottsak. Además, se envía con * cada solicitud. * Cada imagen. Cada script Cada ... –

+0

@cottsak y @Craig: Estoy tratando de almacenar solo las notificaciones de visualización como "su mensaje ha sido enviado". No estoy almacenando datos confidenciales allí. –

Respuesta

3

Hola yo también tenía el mismo problema y que era un problema con la implementación de CookieTempDataProvider.

Modifiqué el código y ahora funciona perfectamente.

Cuando lee los datos de la cookie, los elimina de la solicitud y la respuesta. Pero agregue otra cookie con un valor vacío en la función SaveData que se llama cuando se completa el procesamiento de la solicitud.

Puntos a tener en cuenta: si desea eliminar una cookie, debe establecer el valor de tiempo de espera y enviarla de vuelta al cliente y luego el navegador la eliminará. No podemos hacerlo de otra manera desde el código. La cookie es manejada por el navegador

Y descubrí que al establecer la caducidad en DateTime.MinValue no caduca la cookie en chrome (no sé sobre los otros navegadores) así que me puse a 2001-01-01 :)

Aquí está el código de trabajo

public class CookieTempDataProvider : ITempDataProvider 
{ 
    internal const string TempDataCookieKey = "__ControllerTempData"; 
    HttpContextBase _httpContext; 

    public CookieTempDataProvider(HttpContextBase httpContext) 
    { 
     if (httpContext == null) 
     { 
      throw new ArgumentNullException("httpContext"); 
     } 
     _httpContext = httpContext; 
    } 

    public HttpContextBase HttpContext 
    { 
     get 
     { 
      return _httpContext; 
     } 
    } 

    protected virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) 
    { 
     if (_httpContext.Request.Cookies.AllKeys.Contains(TempDataCookieKey)) //we need this because 
     //Cookies[TempDataCookieKey] will create the cookie if it does not exist 
     { 
      HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey]; 
      if (cookie != null && !string.IsNullOrEmpty(cookie.Value)) 
      { 
       IDictionary<string, object> deserializedTempData = DeserializeTempData(cookie.Value); 

       // Remove cookie     
       cookie.Expires = new DateTime(2000, 1, 1); 
       cookie.Value = string.Empty; 
       _httpContext.Request.Cookies.Remove(TempDataCookieKey); 

       if (_httpContext.Response != null && _httpContext.Response.Cookies != null) 
       { 
        HttpCookie responseCookie = _httpContext.Response.Cookies[TempDataCookieKey]; 
        if (responseCookie != null) 
        { 
         // Remove cookie 
         cookie.Expires = new DateTime(2000, 1, 1); 
         cookie.Value = string.Empty; 
         _httpContext.Response.Cookies.Remove(TempDataCookieKey); 

        } 
       } 

       return deserializedTempData; 
      } 
     } 
     return new Dictionary<string, object>(); 
    } 

    protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 
    { 
     if (values != null && values.Count > 0) 
     { 
      //there are values to set, so add the cookie. But no need to expire it as we need the browser to send the 
      //cookie back with the next request 
      string cookieValue = SerializeToBase64EncodedString(values); 
      var cookie = new HttpCookie(TempDataCookieKey); 
      cookie.HttpOnly = true; 
      cookie.Value = cookieValue; 

      _httpContext.Response.Cookies.Add(cookie); 
     } 
     else 
     { 
      //Still we need to add the cookie with the expiration set, to make the client browser remove the cookie from the request. 
      //Otherwise the browser will continue to send the cookie with the response 

      //Also we need to do this only if the requet had a tempdata cookie 

      if (_httpContext.Request.Cookies.AllKeys.Contains(TempDataCookieKey)) 
      { 
       { 
        HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey]; 

        // Remove the request cookie     
        cookie.Expires = new DateTime(2000, 1, 1); 
        cookie.Value = string.Empty; 
        _httpContext.Request.Cookies.Remove(TempDataCookieKey); 

        var rescookie = new HttpCookie(TempDataCookieKey); 
        rescookie.HttpOnly = true; 
        rescookie.Value = ""; 
        rescookie.Expires = new DateTime(2000, 1, 1); //so that the browser will remove the cookie when it receives the request 
        _httpContext.Response.Cookies.Add(rescookie); 
       } 
      } 
     } 
    } 

    public static IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData) 
    { 
     byte[] bytes = Convert.FromBase64String(base64EncodedSerializedTempData); 
     var memStream = new MemoryStream(bytes); 
     var binFormatter = new BinaryFormatter(); 
     return binFormatter.Deserialize(memStream, null) as IDictionary<string, object> /*TempDataDictionary : This returns NULL*/; 
    } 

    public static string SerializeToBase64EncodedString(IDictionary<string, object> values) 
    { 
     MemoryStream memStream = new MemoryStream(); 
     memStream.Seek(0, SeekOrigin.Begin); 
     var binFormatter = new BinaryFormatter(); 
     binFormatter.Serialize(memStream, values); 
     memStream.Seek(0, SeekOrigin.Begin); 
     byte[] bytes = memStream.ToArray(); 
     return Convert.ToBase64String(bytes); 
    } 

    IDictionary<string, object> ITempDataProvider.LoadTempData(ControllerContext controllerContext) 
    { 
     return LoadTempData(controllerContext); 
    } 

    void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 
    { 
     SaveTempData(controllerContext, values); 
    } 
} 
2

he aquí un ejemplo de una solución de trabajo sin una gran cantidad de exceso de código. Utiliza Json.NET para la serialización, que es más rápido que BinaryFormatter + Base64Encoding y también produce un mucho cadena más corta (= menos sobrecarga HTTP).

public class CookieTempDataProvider : ITempDataProvider 
{ 
    const string cookieKey = "temp"; 

    public IDictionary<string, object> LoadTempData(ControllerContext controllerContext) 
    { 
     var cookie = controllerContext.HttpContext.Request.Cookies[cookieKey]; 

     if (cookie != null) { 
      return JsonConvert.DeserializeObject<IDictionary<string, object>>(cookie.Value); 
     } 

     return null; 
    } 

    // Method is called after action execution. The dictionary mirrors the contents of TempData. 
    // If there are any values in the dictionary, save it in a cookie. If the dictionary is empty, 
    // remove the cookie if it exists. 
    public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) 
    { 
     var ctx = controllerContext.HttpContext; 

     if (values.Count > 0) { 
      var cookie = new HttpCookie(cookieKey) 
      { 
       HttpOnly = true, 
       Value = JsonConvert.SerializeObject(values) 
      }; 

      ctx.Response.Cookies.Add(cookie); 
     } else if (ctx.Request.Cookies[cookieKey] != null) { 

      // Expire cookie to remove it from browser. 
      ctx.Response.Cookies[cookieKey].Expires = DateTime.Today.AddDays(-1); 
     } 
    } 
} 
+0

deberías envolver algunos trycatches alrededor de tu deserialización –

4

Hay una mejor solución por Brock Allen en GitHub que utiliza el cifrado, 2 formas de serialización, y la compresión para proteger y optimizar las cookies.

https://github.com/brockallen/CookieTempData

Aquí hay un enlace al blog al respecto:

http://brockallen.com/2012/06/11/cookie-based-tempdata-provider/

También tiene una buena técnica que utiliza IControllerFactory para asegurar que cada controlador se suministra con una instancia de ITempDataProvider.

Cuestiones relacionadas