SSRS: Почему SKA файлы cookie накапливаются до тех пор, пока не появится "HTTP 400 Bad Request - Request Too Long"?

Я включил SQL-Server Reporting Services 2012 (SSRS 2012) для создания аутентификации, чтобы мы могли использовать ее через Интернет.

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

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

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

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

"HTTP 400 Bad Request - Request Too Long"

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

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

SSRS Sucks

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

Все, что я делаю в новых формах-аутентификации, которые имеют отношение к файлам cookie, следующее:

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 forms-authentication-cookie до 720 минут, которое я ввел в раздел проверки подлинности форм в файле web.config.

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

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

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

Ответ 1

Проблема указана как исправленная в SQL Server 2012 SP1 CU7. (см. комментарии Microsoft в соединить вопрос)
Но все же присутствует в SQL-Server 2014.


В последнем разделе применяется, если вы не можете установить SQL Server 2012 SP1 CU7:

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

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

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

Файлы cookie являются HttpOnly и истекают при закрытии браузера (сеансовые куки).
Это незащищенные файлы HttpOnly, поэтому я потерпел неудачу в попытке удалить их с помощью 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">

Ответ 3

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

Решения, с которыми я пытался работать не:

  • Внедрение Stefan исправления кода. На код сервера на моей странице не удалось получить доступ к файлам cookie, установленным во встроенном документе iframe.
  • Изменение файлов cookie из родительского документа в javascript. По понятным причинам безопасности я не смог получить доступ к файлам cookie в iframe из кода на стороне клиента либо
  • Пробовал пропустить параметры в URL-адрес отчета, чтобы сказать, чтобы он не поддерживал сеанс - пробовал добавить "& rs: KeepSessionAlive = False", который не вызывал ошибки, но не работал
  • * Игрушка * с идеей впрыскивания javascript в самих отчетах. Учитывая, что это связано с изменением некоторых 50-нечетные отчеты и прикручивание экспортированных/сохраненных отчетов, это не вариант

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

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

Итак, здесь , что DID работает для меня:

Я только что добавил код cookie на стороне клиента, который я нашел в другом месте ниже, чтобы удалить все файлы cookie на странице ReportViewer и все неожиданно работает! Только один файл cookie за один раз!

<%@ 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>