URL片段作为SAML令牌身份验证的一部分丢失;变通方法/标准模式?

时间:2013-12-11 15:05:23

标签: gwt saml-2.0 ws-federation gwt-places uri-fragment

一些Web应用程序身份验证协议(如WS-Federation和SAML协议,即所谓的“被动”协议,显然还有ASP.NET Forms身份验证,请参阅this StackOverflow question和AppEngine,请参阅{{ 3}})丢失原始的“URL片段”,即#-sign之后的部分。

大致如下:在干净的浏览器中(所以没有缓存的信息/ cookie /登录信息)我打开URL(1)this GWT bug comment。这使浏览器请求(2)http://example.com/myapp/somepage?some=parameter#somewhere,服务器将我重定向到我的身份提供者(包括身份验证请求中的URL(2)),最终我被重定向回我来自的地方,即URL (2):这是服务器知道的唯一URL。但我想转到URL(1),并且URL片段('anchor')在此过程中已经丢失,实际上已经是第一步了。

这似乎是这些协议的基本限制,因为服务器根本不会看到URL片段。

我知道根据规范,浏览器从服务器请求(2),当我导航到(1)时,导致这个片段丢失限制SAML协议,WS-Federation等。我的问题是:我如何解决这个限制?

明显的解决方法是避免使用http://example.com/myapp/somepage?some=parameter中建议的网址碎片。但是,对于我们的特定Web应用程序并不好,因为我们在单页GWT应用程序中使用可收藏的URL片段,以确保我们的应用程序中的导航不会导致页面重新加载。

我的问题:这种情况还有哪些其他解决方法或标准模式?

(我对GWT + SAML协议解决方案特别感兴趣。)

2 个答案:

答案 0 :(得分:1)

您基本上有两个选择:

  • 避免使用location.hash(使用HTML5的pushState代替,至少在支持它的浏览器上;和/或提出在应用中生成永久链接的方法 - Google网上论坛会这样做)

  • 使用JavaScript进行重定向。即而不是从服务器发送重定向,发送一个空的HTML页面,其中包含一些采用完整URL(使用哈希)的脚本,并使用location.assign()location.replace()进行重定向。运气不错(取决于服务器),您将在身份验证后重定向到该完整的URL。

您当然可以同时执行这两项操作:如果链接是应用程序的深层链接,则执行重定向(即假设没有哈希),否则使用JS发送页面以确保您不会丢失哈希中的任何

最后显而易见的第三个解决方案,远非理想:不做任何事情,并尝试教育用户,当他们需要(重新)进行身份验证时,他们应该重新粘贴URL或重新点击链接或重新 - 点击书签。

答案 1 :(得分:0)

根据RFC 1738,在请求资源时,客户端不会将锚标记发送到服务器。

Anchor标记用于标识资源中的位置,而不是服务器上的不同资源。为了识别资源中的位置,客户端需要从服务器获取完整的资源,并且此过程不需要传输有关片段的信息(因为它对服务器没有任何意义)。

如果您确实希望将片段字符(#)发送到服务器,那么您需要在查询字符串中对其进行编码,否则客户端(浏览器)将在发送时忽略该URL部分请求服务器。

修改

我不知道任何真正的解决方案,但要解决此问题,您需要在客户端的某个地方保存完整的返回URL(带锚标记),因为服务器对锚点一无所知。为此,您可以使用SessionStorage(http://www.w3schools.com/html/html5_webstorage.asp)临时存储ReturnUrl,直到登录过程完成。请注意,旧浏览器不支持它(例如< = IE7)。

在这种情况下,解决方法看起来像这样:

<script>
    if(typeof(sessionStorage) == 'undefined')
    {
        sessionStorage = {
            getItem: function(){},
            setItem: function(){}
        };
    }

    window.onload = function ()
    {
        var key = 'ReturnUrl';

        //try to get last returnUrl with anchors
        var returnUrl = sessionStorage.getItem(key);

        //if we got something, do the navigation
        if(returnUrl !== undefined && returnUrl !== document.URL)
        {
            //clean it up
            sessionStorage.setItem(key, null);
            //navigate to last URL
            window.location = returnUrl;
        }
        else
        {
            //store url 
            sessionStorage.setItem(key, document.URL);
        }
    }
</script>

PS。如果有一些语法错误,请跟我一起,因为我从头顶写下来并没有尝试过。