12

SQL-Server Reporting Services 2012 (SSRS 2012) をフォーム認証に切り替えて、インターネット経由で使用できるようにしました。

SSRS 2012 のフォーム認証のサンプルがどこにも見つからなかったので、SSRS 2008R2 のサンプルを使用して、シングル サインオン (SSO) 用に 2012 に適合させる必要がありました。

その時点で、すべてが期待どおりに機能しているように見えました。ドメイン間でSSOを機能させることさえできました。

しかし今、私は問題を抱えています:

HTML が非 IE5-QuirksMode で正しく表示されるように、td border-size を変更する小さな JavaScript を挿入する必要があったため、Google Chrome ですべてのレポート (200 以上) をテストしていました。約50回目のレポートの後、私は突然得ました:

「HTTP 400 不正なリクエスト - リクエストが長すぎます」

その後、以前は機能していたレポートでさえ、他のレポートを表示できなくなりました。

この問題は Cookie が多すぎることが原因のようです。実際、いくつかの「*_SKA」(Session Keep Alive?) Cookie を削除すると、再び機能し始めました。

SSRSは吸う

私の問題は、この「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」は 1 つしかないため、これがフォーム認証のバグである可能性はないと思います。

問題はSKA Cookieのようです.AFAIKはフォーム認証とは何の関係もなく、すべてバニラSSRSと関係があります.

この理由として私が見ることができる他の唯一のことは、web.config ファイルのフォーム認証セクションに入力したフォーム認証クッキーのタイムアウトを 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 をインストールできない場合は、後のセクションが適用されます。

OK、自分で答えを得ました。

キープアライブ 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

私たちのサイトのアーキテクチャが原因で、この問題に対するさまざまなソリューションを実装するのに非常に苦労していました。何らかの理由で、私の同僚は当初、ReportViewer コントロールではなく、レポートへのリンクを含む iframe を使用することに決めていました。これは、単純な 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"ドキュメントが含まれていることがわかりました。

そして、あなたは何を知っていますか?これは、独自の JavaScript を追加できるヘッダーを備えた単純な ASP.NET ページです!?

だから、ここに私のために働くDIDがあります:

以下の他の場所で見つけたクライアント側の Cookie 設定コードを追加して、ReportViewer ページのすべての Cookie を削除したところ、すべてが突然機能しました。キープアライブ Cookie は一度に 1 つだけです。

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

このページには、置き換えていない既存のコードがいくつかあったことに注意してください。

私はしばらくこれに苦労していたので、これが他の誰かに役立つことを願っています!

注: 私の場合、Session Keep Alive (SKA) Cookie は HTTP 専用ではなかったため、クライアント側からアクセスできましたが、レポート サーバー自体内のクライアント側のみでした。 ここに画像の説明を入力

于 2016-02-09T16:59:04.527 に答える