11

Я включил SQL-Server Reporting Services 2012 (SSRS 2012), чтобы сформировать аутентификацию, чтобы мы могли использовать ее через Интернет.SSRS: Почему SKA-файлы cookie накапливаются до тех пор, пока не появится «HTTP 400 Bad Request - Request Too Long»?

Я не смог найти образец проверки подлинности форм для SSRS 2012 в любом месте, поэтому мне пришлось взять SSRS 2008R2 один и адаптировать его для 2012 года для Single-Sign-On (SSO).

В этот момент все, казалось, работало должным образом; Мне даже удалось заставить SSO работать через домены.

Но теперь у меня есть проблема:

Я проверял все отчеты (более 200) с Google Chrome, потому что я должен был вставить немного JavaScript, который изменяет Td границы размера для что HTML отображается прямо в без IE5-QuirksMode. После того, как о 50-м докладе, я вдруг получил:

«HTTP 400 Bad Request - запрос слишком длинный»

После этого я не мог просматривать любой другой отчет, даже не те, которые сделали работу ранее.

Проблема, похоже, вызвана слишком большим количеством файлов cookie, и действительно, когда я удалил несколько куки «* _SKA» (Session Keep Alive?), Он снова начал работать.

SSRS Sucks

Моя проблема сейчас в том, что я не знаю, что вызывает этот «переполнение печенья». Я также не знаю, если это ошибка в Chrome, ошибка в ванильном SSRS или ошибка, вызванная аутентификацией новых форм.

Все я в новом форм-аутентификации, которая имеет что-то делать с печеньем это:

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; 
     } 


    } 


} 

И как этот код создает «sqlAuthCookie», что один видит в нижней части. Существует только один «sqlAuthCookie», поэтому я не думаю, что это может быть ошибка проверки подлинности формы.

Проблема заключается в том, что файлы cookie SKA, что AFAIK не имеют ничего общего с проверкой подлинности форм и всем, что связано с Vanilla SSRS.

Единственное, что я вижу в качестве причины для этого, - это изменение тайм-аута проверки подлинности-cookie-файлов до 720 минут, которое я ввел в раздел проверки подлинности форм в файле web.config.

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

Кто-нибудь знает, что я могу сделать, чтобы предотвратить получение затопленной сессии Keep-Alive печенье (для удаления этих файлов вручную, за исключением)?

Это не проблема для меня сама по себе, независимо от его высокой степени раздражает, но это будет проблемой, потому что пользователи, вероятно, не будет очень понимание того, что ...

ответ

8

Выпуск внесен в список фиксированных в SQL Server 2012 SP1 CU7. (см. Комментарии от Microsoft в connect issue)
Но все еще присутствует в SQL-Server 2014.


Поздний раздел применяется, если не удается установить SQL Server 2012 с пакетом обновления 1 CU7:

ОК, получил ответ сам.

Файл cookie keep-alive выдается каждый раз, когда открывается отчет.
Теперь это становится проблемой, когда вы открываете (или обновляете или изменяете на другую страницу), скажем, более 110 - 120 отчетов, не закрывая браузер.

Таким образом, мы защищаем, удаляя лишние куки и устанавливаем безопасную границу в appx. 1/2 от предполагаемого максимума в 120 файлов cookie.

Печеньки HttpOnly и заканчиваются при закрытии браузера (сеансовые файлы cookie).
Они не защищены HttpOnly cookies, поэтому я потерпел неудачу в попытке удалить их с помощью JavaScript.
Поэтому возникает необходимость удалить их на стороне сервера. Поскольку мы не можем изменить ReportServer, мы должны использовать встроенные скрипты.

<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://stackoverflow.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"> 
+0

Закрытие браузера не работает для меня с хромом. Мне пришлось вручную удалить файлы cookie. В ближайшее время я попробую ваше решение на стороне сервера. Благодаря! – kravits88

+0

Это не сработало для меня, пока я не изменил 'c.Path = Request.ApplicationPath +"/Pages ";' to 'c.Path = Request.Cookies [i] .Path;' – masty

+0

@masty: Funny , Request.Cookies [i] .Path не работает для меня. Также c.Path = Request.Cookies [i] .Path + "/ Pages"; Вы установили ServicePack 1 + последние кумулятивные обновления? –

6

Вы можете установить KeepSessionAlive ложь о контроле ReportViewer http://msdn.microsoft.com/en-us/library/microsoft.reporting.webforms.reportviewer.keepsessionalive(v=vs.100).aspx

+0

Приятная находка. Я попробую. –

+0

FYI, если вы используете «стандартный» ReportViewer, этот параметр можно изменить в Reporting Services \ ReportServer \ Pages \ ReportViewer.aspx, добавив «KeepSessionAlive =» false »к тегу RS: ReportViewerHost. –

+0

@graham mendick: С другой стороны, если вы это сделаете, вы получите сообщение об ошибке с истекшим сроком действия, если вы нажмете на экспорт через 5-10 минут бездействия. –

3

у меня было много трудностей, реализующий различные решения этой проблемы из-за архитектуры нашего сайта - по какой-либо причине, мои коллеги были изначально решил использовать iframe со ссылками на отчеты вместо элемента управления ReportViewer, и мне было неинтересно пытаться изменить это так поздно в процессе разработки из-за простой проблемы с файлами cookie.

Solutions Я попытался, которые не работа:

  1. Реализация Stefan «s фонового кода исправить - Серверный код на моей странице не может получить доступ к кукам быть установлен во встроенном IFrame документе
  2. Изменение файлов cookie из родительского документа в javascript - По понятным причинам безопасности я не смог получить доступ к файлам cookie в iframe из кода на стороне клиента либо
  3. Пробовал передавать параметры в URL отчета, чтобы сказать ему, чтобы не держать сессию в живых - Пробовал добавлением «& RS: KeepSessionAlive = False», который не приводит к ошибке, но не работает
  4. * игравшего * с идея инъекционного javascript into the reports themselves - Учитывая это потребует изменений некоторых 50-нечетных отчетов и завинчивания экспортируемых/сохраненных отчетов особенности, это не вариант

Наконец, после того, как ковыряться сервером, я понял, что Папка «Страницы» сервера отчетов (C: \ Program Files \ Microsoft SQL Server \ MSRS1 1.SQLEXPRESS \ Reporting Services \ ReportServer \ Pages) содержал документ "ReportViewer.aspx".

И что вы знаете? Это простая страница ASP.NET с заголовком, где вы можете добавить свой собственный javascript !?

Итак, вот , что работал для меня:

Я добавил ниже client-side cookie-setting code I had found elsewhere, чтобы удалить все куки на странице ReportViewer, и вдруг все сработало! Только один куки-хранитель за один раз!

<%@ 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)); 
 
\t 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>

Пожалуйста, обратите внимание, что есть некоторые предварительно существующий код на странице, которую я не заменить.

Надеюсь, это поможет кому-то еще, поскольку я боролся с этим на какое-то время!

Примечание: Пожалуйста, обратите внимание, что в моем случае, Session Keep Alive (SKA) печенье было не HTTP-только, так что я был в состоянии получить доступ к ним с клиентской стороны, хотя только на стороне клиента внутри самого сервера отчетов. enter image description here

+1

Вы можете использовать более новую версию SQL-сервера. Я должен был использовать только HTTP-файлы cookie, марку cookie, которую вы не можете установить с помощью JavaScript. Сначала я попробовал это, и это не получилось. Возможно, они изменили свою реализацию. Мой код применяется к SQL Server Server ReportingServices, версия 11.0.5343.0. –

+0

Да, я только прибегал к этому решению, потому что куки SKA оказались не только HTTP-only. У меня есть пользовательский файл cookie для проверки. Я перехожу с сайта хостинга в SSRS, который является HTTP-only, поэтому я был удивлен, увидев, что keep-alives были разными. Я также использую SSRS 2012, версия 11.0.2100.60. Не уверен, что разница связана с версией или другой конфигурацией. – DicreetAndDiscrete

+0

На какой странице вы вносите эти изменения? ReportViewer.aspx, который живет в C: \ Program Files \ Microsoft SQL Server \ MSRS13.MSSQLSERVER \ Reporting Services \ ReportServer \ Pages, например? –