12

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

我在任何地方都找不到 SSRS 2012 的表单身份验证示例,因此我不得不采用 SSRS 2008R2 一个,并将其调整为 2012 年的单点登录 (SSO)。

那时一切似乎都按预期进行。我什至设法让 SSO 跨域工作。

但是现在我有一个问题:

我正在使用 Google Chrome 测试所有报告(超过 200 个),因为我必须插入一些 JavaScript 来改变 td 边框大小,以便 HTML 在非 IE5-QuirksMode 下正确显示。大约在第 50 次报告之后,我突然得到:

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

在那之后,我无法查看任何其他报告,即使是之前确实有效的报告。

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

SSRS 很烂

我现在的问题是我不知道是什么导致了这个“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”,所以我认为这不可能是表单身份验证错误。

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

我能看到的唯一其他原因是我在 web.config 文件的 forms-authentication 部分中输入的 forms-authentication-cookie 超时更改为 720 分钟。

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

有谁知道我可以做些什么来防止被会话保持活动 cookie 淹没(除了手动删除这些 cookie)?

这对我本身来说没有问题,除了它非常烦人之外,但这将是一个问题,因为用户可能不会很理解这一点......

4

3 回答 3

8

在 SQL Server 2012 SP1 CU7中列为已修复的问题。(请参阅 Microsoft 在连接问题中的评论)
但仍然存在于 SQL-Server 2014 中。


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

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

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

所以我们通过删除多余的cookie来保护,并在appx设置一个安全边界。假设最多 120 个 cookie 的 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">
于 2013-08-30T08:02:38.810 回答
6

您可以在 ReportViewer 控件上将 KeepSessionAlive 设置为 false http://msdn.microsoft.com/en-us/library/microsoft.reporting.webforms.reportviewer.keepsessionalive(v=vs.100).aspx

于 2014-03-27T12:15:07.560 回答
4

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

我尝试过的解决方案不起作用

  1. 实施Stefan的代码隐藏修复- 我页面上的服务器代码无法访问嵌入 iframe 文档中设置的 cookie
  2. 从 javascript 中的父文档更改 cookie - 出于可以理解的安全原因,我也无法从客户端代码访问 iframe 中的 cookie
  3. 尝试将参数传递到报告 URL 以告诉它不要保持会话处于活动状态 - 尝试附加“&rs:KeepSessionAlive=False”,这不会导致错误,但不起作用
  4. *玩弄*将javascript 注入报告本身的想法- 考虑到这将涉及更改一些 50 多个报告并搞砸导出/保存的报告功能,这不是一个选项

最后,在浏览了服务器之后,我意识到报表服务器“Pages”文件夹 (C:\Program Files\Microsoft SQL Server\MSRS11.SQLEXPRESS\Reporting Services\ReportServer\Pages)包含一个“ReportViewer.aspx”文档。

你知道什么?它只是一个带有标题的简单 ASP.NET 页面,您可以在其中添加自己的 javascript!?

所以,这就是对我有用的东西:

我刚刚添加了我在下面其他地方找到的客户端 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>

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

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

注意: 请注意,在我的情况下,会话保持活动 (SKA) cookie 不是仅 HTTP,因此我能够从客户端访问它们,尽管只能从报表服务器本身的客户端访问。 在此处输入图像描述

于 2016-02-09T16:59:04.527 回答