ADFS会话过期并导致错误

时间:2013-03-27 20:27:15

标签: ajax adfs

我们将ADFS用于内部应用程序 - 用户在访问我们的某个应用程序时,基本上都是透明登录的。但是,如果用户将页面打开超过一个小时然后尝试在该页面上执行某些操作(除了导航到另一个页面),则会出现错误:

  

此页面正在访问不受其控制的信息。这带来了安全风险。你想继续吗?

似乎页面正在尝试将该请求重定向到ADFS服务器,并且浏览器阻止了该请求。

我的问题是:如何捕获这种情况并让用户到ADFS服务器重新进行身份验证?

我没有幸运在谷歌上发现任何关于此的事情。

2 个答案:

答案 0 :(得分:3)

更新:以下解决方案取决于iframe。 ADFS 3.0的X-Frame-Options默认为DENY,无法更改设置。因此,此解决方案仅适用于ADFS 2.1&早。

在你的global.asax.cs中,你将要捕获任何中期AJAX 302并将它们变成401 Unauthorized。这将阻止调用继续(并弹出该消息),并将我们发送到$(document).ajaxError()。

    protected void Application_EndRequest()
    {
        var context = new HttpContextWrapper(this.Context);
        if (context.Response.StatusCode == 302 && context.Request.IsAjaxRequest())
        {
            context.Response.Clear();
            context.Response.StatusCode = 401;
        }
    }

然后,在那里,拦截任何401s,然后再进行其余的错误处理。我选择向用户显示消息。您可以在此处执行下一步,但为了便于阅读,我将ajaxSettings对象发送到另一个函数。返回true,这样它就不会进入其余的错误处理。

如果要双重检查这是ADFS,event.target.referrer将具有尝试重定向的URL。

$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
    if (xhr.status == 401) { 
        alert("Your session has timed out. Click OK to reauthorize and extend your session.");

        TriggerReauthenticationRefresher(ajaxSettings); 
        return true;
    }
…the rest of the error handling code…            
});

我的页面中有一个空div,仅用于这种情况,id为'refresherBox',但你可以在DOM中的任何元素上执行此操作。将iframe放在您域中的某个虚拟页面中。在我的例子中,ADFSRefresher.cshtml的内容只是

 <div><input type="hidden" value="@DateTime.Now.ToString()" /></div>

我没有使用全局变量,而是使用.data()存储ajaxSettings。我们还需要跟踪iframe重新加载的次数,因此我们还要存储loadcount。将iframe插入DOM,它将启动。

function TriggerReauthenticationRefresher(ajaxSettings) {
    var refreshframe = '<iframe src="@Url.Action("ADFSRefresher", "Debug")" style="display:none" onload="TrackFrameReloads()" />';

    $('#refresherBox').data('loadcount', 0);
    $('#refresherBox').data('originalRequestSettings', ajaxSettings);

    $('#refresherBox').html(refreshframe);
}

每次iframe完成加载时,都会触发TrackFrameReloads。由于我们知道存在即将发生的ADFS重定向,因此它将触发两次。第一次是重定向,第二次是src url。所以它第一次触发时,我们只是增加loadcount。

第二次发射,我们知道我们已成功重新认证。检索ajaxSettings,清除存储的数据,然后您可以重新使用原始设置发送AJAX调用!它将通过,未重定向,并运行其原始的成功&amp;完整的功能。

function TrackFrameReloads() {
    var i = $('#refresherBox').data('loadcount');
    if (i == 1) {
        alert('Your session has been extended.');

        var ajaxSettings = $('#refresherBox').data('originalRequestSettings');

        $('#refresherBox').removeData();

        $.ajax(ajaxSettings);

    } else {
        $('#refresherBox').data("loadcount", 1);
    }
}

请注意,如果您定义了它们,则错误和完整功能将被触发。

如果您愿意,可以跳过向用户发送的两条警报消息。根据您的ADFS设置,这应该只需1秒钟,并且用户不必被告知发生任何这种情况!

答案 1 :(得分:1)

您可以在global.asax中手动检查和重新发出安全令牌,并使用它来创建滑动会话。通过滑动会话,您可以选择推迟重新认证,直到它变为“安全”状态。这样做(当由于ADFS重定向而不再丢失数据时)。

在SessionSecurityTokenReceived事件中,您可以评估令牌和请求。如果令牌已过期并且请求是将从重定向中丢失数据的请求,则可以重新发布新的&#34;临时&#34;令牌。新令牌应该具有非常短的寿命,只需足够长的时间,以便您可以安全地完成当前请求。然后令牌将过期,并在下一个请求时再次进行评估。

protected void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
{
    var now = DateTime.UtcNow;
    SessionSecurityToken token = e.SessionToken;
    var httpContext = new HttpContextWrapper(this.Context);

   if (now > token.ValidTo
       && (httpContext.Request.IsAjaxRequest() || httpContext.Request.HttpMethod == "POST"))
   {
       var sessionAuthModule = (SessionAuthenticationModule)sender;
       e.SessionToken = sessionAuthModule.CreateSessionSecurityToken(token.ClaimsPrincipal,
                                                                     token.Context,
                                                                     now,
                                                                     now.AddMinutes(2),
                                                                     token.IsPersistent);
       e.ReissueCookie = true;
   }
}

ADFS会话将继续推迟重新认证,直到下一个GET请求。然后重定向将最终发生,并且将向用户发出正常生命周期的正确令牌。