一些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协议解决方案特别感兴趣。)
答案 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。如果有一些语法错误,请跟我一起,因为我从头顶写下来并没有尝试过。