2013-03-27 115 views
7

我們將ADFS用於我們的內部應用程序 - 用戶基本上都會在透明地登錄到我們的某個應用程序中。但是,如果用戶開了一個網頁了一個多小時,然後嘗試做網頁上的東西(除了導航到另一個頁面),他們得到一個錯誤:ADFS會話過期並導致錯誤

This page is accessing information that is not under its control. This poses a security risk. Do you want to continue?

好像在網頁正試圖將該請求重定向到ADFS服務器,並且瀏覽器正在阻止該請求。

我的問題是這樣的:我如何捕獲這種情況並讓用戶到ADFS服務器重新進行身份驗證?

我還沒有在Google上找到任何有關此問題的運氣。

+0

如果它是相關的,點擊「是」繼續將用戶帶到行動 - 這不是作爲一個GET存在,所以他們然後得到一個404. – zimdanen 2013-03-27 20:28:10

+0

你找到任何整潔的解決方案?在我們的例子中,我們正在考慮(如果我們可以檢測到問題發生),在訪問相關內部應用程序中新頁面的頁面上添加一個新的(隱藏的)iframe。將該新頁面加載到'iframe'應該會觸發ADFS服務器的完全被動流程,最終將在重新認證後加載新頁面。此時,新頁面將通過「postMessage」通知現有頁面循環已完成,並且適當的cookie應該再次可用。 – 2013-08-29 12:30:14

+1

@Damien_The_Unbeliever:由於時間問題和其他優先事項,我們沒有繼續深入探討。 iframe解決方案對我來說很不好,但是,如果它是解決問題的唯一方法,那就是它。讓我知道它是如何解決你的。 – zimdanen 2013-08-29 13:02:33

回答

1

您可以在global.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請求。然後重定向最終會發生,並且用戶將被髮出正常壽命的正確標記。

+0

我在最初問題的相同情況,我正在嘗試此解決方案。我必須安裝哪個nuget包?此處理程序永遠不會被解僱 – 2017-01-20 10:47:46

+0

這使用Microsoft令牌驗證擴展包(System.IdentityModel.Tokens.ValidatingIssuerNameRegistry) – friggle 2017-01-20 19:49:19

3

更新:以下解決方案取決於iframe。 ADFS 3.0的X-Frame-Options默認爲DENY,無法更改設置。因此,此解決方案僅適用於早期的ADFS 2.1 &。

在你的global.asax.cs中,你會想要捕獲任何mid-AJAX 302s並將它們變成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只是針對這種情況,與「refresherBox」的ID,但你可以做到這一點DOM中的任何元素。把一個iframe放到你的域中的某個虛擬頁面上。就我而言,ADFSRefresher.cshtml的內容都只是

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

而不是使用全局變量,我使用。數據()存儲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調用!它將通過,不重定向,並運行其原始成功&完整的功能。

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秒鐘,並且用戶不必被通知任何這發生!

+0

這有效,但我覺得在請求中早些時候知道該會話已過期,然後執行某些操作,而不是分解所有302響應會更好。 – BigOmega 2014-01-31 16:17:52

+0

ADFS將不允許我加載iFrame: 拒絕在框架中顯示,因爲它將'X-Frame-Options'設置爲'DENY'。 任何方法? – severin 2015-02-05 13:29:11

+1

你知道它是哪個版本的ADFS嗎?顯然,ADFS 3將該設置默認爲DENY,並且無法更改它。我會更新我的答案,注意此解決方案僅適用於ADFS 2及更低版本。我實際上已經不再使用發佈的解決方案,而是開始使用滑動會話。這篇文章對於瞭解這一點非常有幫助:http://www.cloudidentity.com/blog/2010/06/16/warning-sliding-sessions-are-closer-than-they-appear/ – friggle 2015-02-05 20:06:16