asp.net cookies reporting-services forms-authentication ssrs-2012

asp.net - SSRS: ¿Por qué las cookies SKA se acumulan hasta que se produce "Solicitud incorrecta HTTP 400-Solicitud demasiado larga"?



reporting-services forms-authentication (3)

He cambiado SQL-Server Reporting Services 2012 (SSRS 2012) a la autenticación de formularios para que podamos usarlo a través de Internet.

No pude encontrar una muestra de autenticación de formularios para SSRS 2012 en ninguna parte, así que tuve que tomar el SSRS 2008R2 y adaptarlo para 2012, para Single-Sign-On (SSO).

En ese momento todo parecía estar funcionando como se esperaba; Incluso logré que SSO funcionara en todos los dominios.

Pero ahora tengo un problema:

Estaba probando todos los informes (más de 200) con Google Chrome, porque tuve que insertar un pequeño JavaScript que altera el tamaño del borde Td para que el HTML se muestre correctamente en un IE-QuirksMode. Después del 50. ° informe, de repente obtuve:

"HTTP 400 Bad Request - Request Too Long"

Después de eso, no pude ver ningún otro informe, ni siquiera los que sí funcionaron anteriormente.

El problema parece ser causado por demasiadas cookies, y de hecho, cuando borré algunas cookies "* _SKA" (Session Keep Alive?), Comenzó a funcionar nuevamente.

Mi problema ahora es que no sé qué causa este "desbordamiento de cookies". Tampoco sé, si esto es un error en Chrome, un error en SSRS vainilla o un error causado por la autenticación de las nuevas formas.

Todo lo que hago en los nuevos formularios: la autenticación que tiene algo que ver con las cookies es esta:

using System; using System.Collections.Generic; using System.Text; namespace FormsAuthentication_RS2012 { internal class FormsAuthenticationWorkaround { public static void RedirectFromLoginPage(string strUser, bool createPersistentCookie) { //string url = System.Web.Security.FormsAuthentication.GetRedirectUrl(strUser, true); string url = GetRedirectUrlWithoutFailingOnColon(strUser, createPersistentCookie); SQL.Log("User: ''" + strUser + "'' ReturnUrl", url); if (System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Response != null) System.Web.HttpContext.Current.Response.Redirect(url); } // https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web.Security/FormsAuthentication.cs // @MSFT: WTF are u guys smoking ? public static string GetRedirectUrlWithoutFailingOnColon(string userName, bool createPersistentCookie) { if (userName == null) return null; System.Web.Security.FormsAuthentication.SetAuthCookie(userName, true, "/"); string returnUrl = null; if (System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Request != null) returnUrl = System.Web.HttpContext.Current.Request.QueryString["ReturnUrl"]; if (returnUrl != null) return returnUrl; returnUrl = System.Web.Security.FormsAuthentication.DefaultUrl; return returnUrl; } } }

Y como este código crea la "sqlAuthCookie" que se ve en la parte inferior. Solo hay una "sqlAuthCookie", así que no creo que esto pueda ser un error de autenticación de formularios.

El problema parece ser las cookies de SKA, que AFAIK no tiene nada que ver con la autenticación de formularios y todo lo relacionado con Vanilla SSRS.

La única otra cosa que pude ver como una razón para esto es el cambio en el tiempo de espera de forms-authentication-cookie a 720 minutos que ingresé en la sección de autenticación de formularios en el archivo web.config.

<authentication mode="Forms"> <forms loginUrl="logon.aspx" name="sqlAuthCookie" timeout="720" path="/"> </forms> </authentication>

¿Alguien sabe qué puedo hacer para evitar que las cookies de Session Keep-Alive (excepto para eliminar esas cookies de forma manual) me inunden?

No es un problema para mí per se, aparte de ser muy molesto, pero va a ser un problema porque los usuarios probablemente no lo entenderán muy bien ...



Problema enumerado como arreglado en SQL Server 2012 SP1 CU7. (ver comentarios de Microsoft en el tema de conexión )
Pero aún está presente en SQL-Server 2014.

La última sección aplica, si no puede instalar SQL Server 2012 SP1 CU7:

OK, obtuve la respuesta yo mismo.

La cookie keep-alive se emite cada vez que se abre un informe.
Ahora, eso se convierte en un problema cuando uno abre (o actualiza, o cambia a otra página), por ejemplo, más de 110 a 120 informes, sin cerrar el navegador.

Así que salvaguardamos eliminando el exceso de cookies y estableciendo un límite seguro en appx. 1/2 del máximo supuesto de 120 cookies.

Las cookies son HttpOnly, y caducan cuando se cierra el navegador (cookies de sesión).
Son cookies HttpOnly no seguras, por lo que no pude en mi intento de eliminarlas a través de JavaScript.
Por lo tanto, es necesario eliminarlos del lado del servidor. Como no podemos modificar ReportServer, tenemos que usar scripts en línea.

<body style="margin: 0px; overflow: auto"> <script type="text/C#" runat="server"> protected string ClearSessionKeepAliveCookiesToPreventHttp400HeaderTooLong() { if(Request == null || Request.Cookies == null) return ""; if(Request.Cookies.Count < 60) return ""; // System.Web.HttpContext.Current.Response.Write("<h1>"+Request.Cookies.Count.ToString()+"</h1>"); for(int i = 0; i < Request.Cookies.Count; ++i) { if(StringComparer.OrdinalIgnoreCase.Equals(Request.Cookies[i].Name, System.Web.Security.FormsAuthentication.FormsCookieName)) continue; if(!Request.Cookies[i].Name.EndsWith("_SKA", System.StringComparison.OrdinalIgnoreCase)) continue; if(i > 60) break; //System.Web.HttpContext.Current.Response.Write("<h1>"+Request.Cookies[i].Name+"</h1>"); System.Web.HttpCookie c = new System.Web.HttpCookie( Request.Cookies[i].Name ); //c.Expires = System.DateTime.Now.AddDays( -1 ); c.Expires = new System.DateTime(1970, 1 ,1); c.Path = Request.ApplicationPath + "/Pages"; c.Secure = false; c.HttpOnly = true; // http://.com/questions/5517273/httpcookiecollection-add-vs-httpcookiecollection-set-does-the-request-cookies //Response.Cookies[Request.Cookies[i].Name] = c; //Response.Cookies.Add(c); Response.Cookies.Set(c); } return ""; } </script> <%=ClearSessionKeepAliveCookiesToPreventHttp400HeaderTooLong()%> <form style="width:100%;height:100%" runat="server" ID="ReportViewerForm">


Estaba teniendo muchas dificultades para implementar diferentes soluciones a este problema debido a la arquitectura de nuestro sitio; por alguna razón, mis compañeros de trabajo habían decidido originalmente usar iframes con enlaces a los informes en lugar de un control ReportViewer, y yo detestaba intentar y cambiar esto tan tarde en el proceso de desarrollo debido a un simple problema de cookies.

Soluciones que probé que no funcionó:

  1. Implementación de la corrección de código subyacente de Stefan : el código del servidor en mi página no podía acceder a las cookies que se configuraban en el documento de iframe incrustado.
  2. Cambio de cookies del documento principal en JavaScript : por razones de seguridad comprensibles, no pude acceder a las cookies en el iframe desde el código del lado del cliente.
  3. Intentó pasar los parámetros en la URL del informe para indicarle que no mantenga viva la sesión : intenté agregar "& rs: KeepSessionAlive = False", lo que no causó un error, pero no funcionó
  4. * Jugueteó * con la idea de inyectar javascript en los informes en sí . Considerando que esto implicaría cambiar unos 50 informes impares y atornillar la función de informes exportados / guardados, esta no era una opción

Finalmente, después de hurgar en el servidor, me di cuenta de que la carpeta "Páginas" del Servidor de informes (C: / Archivos de programa / Microsoft SQL Server / MSRS11.SQLEXPRESS / Reporting Services / ReportServer / Pages) contenía un documento "ReportViewer.aspx" .

Y que sabes? ¿Es solo una página simple de ASP.NET con un encabezado donde puedes agregar tu propio javascript?

Entonces, esto es lo que DID funcionó para mí:

Acabo de agregar el código de configuración de cookies del lado del cliente que había encontrado en otro lugar a continuación para eliminar todas las cookies en la página de ReportViewer, ¡y todo funcionó de repente! ¡Solo una cookie para mantener viva a la vez!

<%@ Register TagPrefix="RS" Namespace="Microsoft.ReportingServices.WebServer" Assembly="ReportingServicesWebServer" %> <%@ Page Language="C#" AutoEventWireup="true" Inherits="Microsoft.ReportingServices.WebServer.ReportViewerPage" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head id="headID" runat="server"> <title><%= GetPageTitle() %></title> </head> <body style="margin: 0px; overflow: auto"> <form style="width:100%;height:100%" runat="server" ID="ReportViewerForm"> <asp:ScriptManager ID="AjaxScriptManager" AsyncPostBackTimeout="0" runat="server" /> <RS:ReportViewerHost ID="ReportViewerControl" runat="server" /> </form> <script language="javascript" type="text/javascript"> // Beginning of inserted cookies management code function createCookie(name, value, days) { if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); var expires = "; expires=" + date.toUTCString(); } else var expires = ""; document.cookie = name + "=" + value + expires; } function readCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split('';''); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == '' '') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); } return null; } function eraseCookie(name) { createCookie(name, "", -1); } var getCookies = function () { var pairs = document.cookie.split(";"); var cookies = {}; for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split("="); cookies[pair[0]] = unescape(pair[1]); } return cookies; } var pairs = document.cookie.split(";"); var cookies = {}; for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split("="); cookies[pair[0]] = unescape(pair[1]); } var keys = []; for (var key in cookies) { if (cookies.hasOwnProperty(key)) { keys.push(key); } } for (index = 0; index < keys.length; ++index) { eraseCookie(keys[index]); } // End of inserted cookies management logic //Beginning of pre-existing code Sys.WebForms.PageRequestManager.prototype._destroyTree = function(element) { var allnodes = element.getElementsByTagName(''*''), length = allnodes.length; var nodes = new Array(length); for (var k = 0; k < length; k++) { nodes[k] = allnodes[k]; } for (var j = 0, l = nodes.length; j < l; j++) { var node = nodes[j]; if (node.nodeType === 1) { if (node.dispose && typeof (node.dispose) === "function") { node.dispose(); } else if (node.control && typeof (node.control.dispose) === "function") { node.control.dispose(); } var behaviors = node._behaviors; if (behaviors) { behaviors = Array.apply(null, behaviors); for (var k = behaviors.length - 1; k >= 0; k--) { behaviors[k].dispose(); } } } } } </script> </body> </html>

Tenga en cuenta que había algún código preexistente en la página que no reemplacé.

Espero que esto ayude a otros, ¡ya que luché con este por un tiempo!

NOTA: Tenga en cuenta que, en mi caso, las cookies de Session Keep Alive (SKA) no eran solo HTTP, así que pude acceder a ellas desde el lado del cliente, aunque solo desde el lado del cliente dentro del servidor de informes.