首页 文章

SSRS:为什么SKA-cookies会在“HTTP 400 Bad Request - Request Too Long”发生之前 Build 起来?

提问于
浏览
11

我已将SQL-Server Reporting Services 2012(SSRS 2012)切换为表单身份验证,以便我们可以通过Internet使用它 .

我无法在任何地方找到SSRS 2012的表单身份验证示例,因此我不得不采用SSRS 2008R2,并针对2012年进行单点登录(SSO) .

在那一刻,一切似乎按预期工作;我甚至设法让SSO跨域工作 .

但现在我有一个问题:

我正在使用谷歌浏览器测试所有报告(超过200个),因为我必须插入一个改变td border-size的JavaScript,因为HTML显示在非IE5-QuirksMode中 . 在大约第50次报告之后,我突然得到了:

“HTTP 400错误请求 - 请求太长”

在那之后,我无法查看任何其他报告,甚至是那些之前没有工作的报告 .

问题似乎是由于太多的cookie造成的,事实上,当我删除一些“* _SKA”(Session Keep Alive?)cookie时,它又开始工作了 .

SSRS Sucks

我现在的问题是我不知道导致这种“cookie溢出”的原因 . 我也不知道,如果这是Chrome中的一个错误,一个vanilla 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”所以我认为这可能不是一个表单身份验证错误 .

问题似乎是SKA cookie,AFAIK与表单身份验证无关,与Vanilla SSRS有关 .

我可以看到唯一的另一个原因是我在web.config文件的forms-authentication部分输入的forms-authentication-cookie timeout更改为720分钟 .

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

有没有人知道我可以做些什么来防止被Session Keep-Alive cookie淹没(除了手动删除这些cookie)?

对我来说这本身没问题,除了它非常讨厌,但它会成为一个问题,因为用户可能不会非常理解......

3 回答

  • 4

    Issue listed as fixed 在SQL Server 2012 SP1 CU7. 中(请参阅Microsoft在connect issue中的评论)
    但仍然出现在SQL-Server 2014中 .


    如果无法安装SQL Server 2012 SP1 CU7,则应用后面的部分:

    好的,我自己得到了答案 .

    每次打开报告时都会发出保持活动的cookie .
    现在,当一个人打开(或刷新或更改另一个页面),例如超过110-120个报告而不关闭浏览器时,这就成了一个问题 .

    因此,我们通过删除多余的cookie来保护,并在appx设置安全边界 . 假定最多120个 Cookies 的1/2 .

    Cookie是HttpOnly,当关闭浏览器时会过期(会话cookie) .
    它们是非安全的HttpOnly cookie,这就是我尝试通过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">
    
  • 6
  • 8

    由于我们网站的体系结构,我在实现这个问题的不同解决方案时遇到了很多困难 - 无论出于何种原因,我的同事最初决定使用带有报告链接的iframe而不是ReportViewer控件,我不愿意尝试改变由于简单的cookie问题,在开发过程中这么晚 .

    我试过的解决方案 did not 工作:

    • Implementing Stefan's code-behind fix - 我页面上的服务器代码无法访问嵌入式iframe文档中设置的cookie

    • Changing cookies from the parent document in javascript - 出于可以理解的安全原因,我无法从客户端代码访问iframe中的cookie

    • Tried passing parameters into the report URL to tell it not to keep the session alive - 尝试追加"&rs:KeepSessionAlive=False",这不会导致错误,但无法正常工作

    • Toyed with the idea of injecting javascript into the reports themselves - 考虑到这将涉及更改大约50多个报告并搞砸导出/保存的报告功能,这不是一个选项

    最后,在浏览服务器之后,我意识到 Report Server "Pages" folder (C:\ Program Files \ Microsoft SQL Server \ MSRS11.SQLEXPRESS \ Reporting Services \ ReportServer \ Pages)包含 "ReportViewer.aspx" 文档 .

    你知道什么? It's just a simple ASP.NET page with a header where you can add your own javascript!?

    所以,这是 what DID work for me:

    我刚刚添加了client-side cookie-setting code I had found elsewhere以删除ReportViewer页面上的所有cookie,一切都突然起作用了!一次只有一个保持活跃的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>
    

    请注意,页面中有一些我没有替换的预先存在的代码 .

    希望这对其他人有所帮助,因为我在这个问题上挣扎了一段时间!

    NOTE: 请注意,在我的情况下,Session Keep Alive(SKA)cookie不仅仅是HTTP,所以我能够从客户端访问它们,尽管只是报告服务器本身的客户端 .

相关问题