日志含义ADFS会话过期,导致错误



我们在内部应用程序中使用ADFS -用户基本上可以在任何时候透明地登录到我们的应用程序之一。但是,如果用户打开一个页面超过一个小时,然后试图在该页上做一些事情(而不是导航到另一个页面),他们会得到一个错误:

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

似乎页面试图将该请求重定向到ADFS服务器,但被浏览器阻止了。

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

我没有在谷歌上找到任何关于这个的东西。

更新:下面的解决方案依赖于iframe。ADFS 3.0的x帧选项默认为DENY,没有选项可以更改设置。因此,此解决方案仅适用于ADFS 2.1 &早些时候。

在您的global.asax.cs中,您将想要捕获任何mid-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;
        }
    }

然后,在那里拦截任何401,然后再进行其余的错误处理。我选择向用户显示一条消息。你可以在这里做下一步,但为了可读性,我将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在我的页面只是为了这种情况,与'refresherBox'的id,但你可以在你的DOM中的任何元素这样做。把一个iframe放到域的某个虚拟页面上。在我的例子中,是ADFSRefresher的内容。CSHTML只是

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

而不是使用全局变量,我存储ajaxSettings使用。data()。我们还需要跟踪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);
}

trackframeloads将在每次iframe加载完成时触发。因为我们知道有一个即将到来的ADFS重定向,它将触发两次。第一次将是重定向,第二次将是到它的src url。第一次触发时,我们增加loadcount。

第二次触发,我们知道我们已经成功地重新验证了。检索ajaxSettings,清除存储的数据,然后您可以重新使用原始设置来发送AJAX调用!它将经过,不重定向,并运行其最初的成功& &;完整的函数。

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);
    }
}

请注意,如果您定义了它们,则error和complete函数将已经被触发。

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

您可以在全局手动检查和重新颁发安全令牌。Asax,并使用它来创建滑动会话。对于滑动会话,您可以选择推迟重新身份验证,直到它变得"安全"(当数据不再因ADFS重定向而丢失时)。

在SessionSecurityTokenReceived事件中,您可以评估令牌和请求。如果令牌过期,并且请求将因重定向而丢失数据,则可以重新发出新的"临时"令牌。新令牌的生命周期应该很短,仅够您安全地完成当前请求。令牌将过期,并在下一个请求时重新计算。

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请求。然后重定向将最终发生,用户将获得正常生命周期的适当令牌。

相关内容

最新更新