ASP.Net MVC 4 的 OAuthWebSecurity 可以打開彈出視窗嗎
我試圖弄清楚如何使用 ASP.Net MVC 4 的新 OAuthWebSecurity 功能。點擊 facebook 或 twitter 外部登錄按鈕時是否可以將表單發佈到彈出視窗而不是刷新目前頁面?在使用 Javascript 之前,我在 Twitter 和 Facebook 中使用了 oauth,外部身份驗證將在彈出視窗中進行。非同步返回結果後,彈出視窗將關閉。我可以使用 MVC 4 的新 OAuthWebSecurity 功能做類似的事情嗎?謝謝。
解決這個問題有幾個方面:
- 打開一個彈出視窗以適應身份驗證序列。
- 驗證完成後關閉彈出視窗。
- 處理身份驗證失敗。
- 更新父頁面以反映使用者已通過身份驗證的事實。
以下是我實現這些要求的方式,使用 MVC4
Internet Application模板作為起點:要在彈出視窗中啟動身份驗證序列(而不是重定向到新頁面),您需要進行修改
_ExternalLoginListPartial.cshtml,以便其表單回發針對由 JavaScript 函式啟動的彈出視窗:@model ICollection<AuthenticationClientData> @if (Model.Count == 0) { <div class="message-info"> <p>There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkId=252166">this article</a> for details on setting up this ASP.NET application to support logging in via external services.</p> </div> } else { <form id="login-launch" action="@Url.Action("ExternalLogin", "Account")" method="POST" target="login-popup" onsubmit="invokeLogin();"> @Html.AntiForgeryToken() <fieldset id="socialLoginList"> <input type="hidden" id="provider" name="provider" /> <input type="hidden" name="returnUrl" value="@ViewBag.ReturnUrl"/> <p> @foreach (var p in OAuthWebSecurity.RegisteredClientData) { <button type="submit" onclick="$('#provider').attr('value', '@p.DisplayName'); $('#login-launch').submit();" title="Log in using @p.DisplayName">@p.DisplayName</button> } </p> </fieldset> </form> } <script type="text/javascript"> function invokeLogin() { var chrome = 100; var width = 500; var height = 500; var left = (screen.width - width) / 2; var top = (screen.height - height - chrome) / 2; var options = "status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=" + left + ",top=" + top + ",width=" + width + ",height=" + height; window.open("about:blank", "login-popup", options); } </script>在目前狀態下,此程式碼正確啟動彈出視窗並允許執行身份驗證序列,但彈出視窗保持打開狀態,如果指定了重定向 URL,則彈出視窗顯示此頁面,而不是將父頁面重定向到此 URL。
要在成功(或失敗)身份驗證後讓彈出視窗自行關閉,需要修改處理身份驗證回調的控制器操作方法,以便它返回包含關閉彈出視窗的 JavaScript 的自定義視圖。正如我們將在下面看到的,這種機制也可以用來實現目標的解決方案
$$ 3 $$和$$ 4 $$多於。
[AllowAnonymous] public ActionResult ExternalLoginCallback(string returnUrl) { var result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl })); if (!result.IsSuccessful) { return View("LoginResult", new LoginResultViewModel(false, Url.Action("ExternalLoginFailure"))); } if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false)) { return View("LoginResult", new LoginResultViewModel(true, returnUrl)); } OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, result.UserName); return View("LoginResult", new LoginResultViewModel(true, returnUrl)); }此操作方法是
ExternalLoginCallback()原始項目模板附帶的方法的簡化版本。與原始實現不同,此簡化範例不支持允許使用者在創建新帳戶時定義個性化使用者名,也不允許將多個 OAuth 或 OpenID 帳戶與單個 MVC 使用者帳戶關聯。但是,通過擴展上述模式以合併原始模板的更複雜邏輯,這些功能是可行的。上述操作方法的一個關鍵設計特徵是它總是返回相同的視圖,而不管身份驗證嘗試的結果如何。這是必要的,因為返回的視圖包含關閉身份驗證彈出視窗並呼叫父頁面中任何必需的後續操作的 JavaScript。因此,如果您修改上述模式,則必須確保每個程式碼路徑都返回一個
LoginResult視圖實例,並根據身份驗證的結果正確填充。這是
Loginresult視圖的標記:@model LoginResultViewModel @{ Layout = null; var success = Model.Success ? "true" : "false"; var returnUrl = Model.ReturnUrl == null ? "null" : string.Format("'{0}'", Model.ReturnUrl); } <!DOCTYPE html> <html> <head> <script type="text/javascript"> if (window.opener && window.opener.loginCallback) { window.opener.loginCallback(@success, @Html.Raw(returnUrl)); } window.close(); </script> </head> </html>上面的視圖接受一個類型的模型,
LoginResultViewModel它反映了已完成的身份驗證嘗試的結果:public class LoginResultViewModel { public LoginResultViewModel(bool success, string returnUrl) { Success = success; ReturnUrl = returnUrl; } public bool Success { get; set; } public string ReturnUrl { get; set; } }有了上述所有元素,就可以啟動一個身份驗證序列,該序列在序列完成時自動關閉的彈出視窗中執行。如果身份驗證成功,使用者將在此時登錄,並且如果使用返回 URL 啟動它(如果由對受
[Authorize]屬性保護的操作方法的請求觸發,則會自動發生),父頁面將被重定向到最初請求的 URL。但是,如果使用者明確啟動身份驗證(例如,通過訪問登錄頁面),父頁面將不會重定向,因此可能需要部分頁面更新以反映使用者現在已登錄的事實。在 MVC 模板範例中,需要更新頁面以顯示使用者名和
Logout按鈕,而不是Login和Register按鈕。這可以通過在佈局視圖中定義一個 JavaScript 回調函式來完成,該回調函式由身份驗證彈出視窗執行的 JavaScript 呼叫:
<script type="text/javascript"> function loginCallback(success, returnUrl) { if (returnUrl) { window.location.href = returnUrl; } else { $.ajax({ url: '@Url.Action("LoginPartial", "Account")', success: function (result) { $('#login').html(result); } }); } } </script>上面的 JavaScript 對呈現並返回現有
_LoginPartial視圖的新操作方法進行 AJAX 呼叫:[HttpGet] public ActionResult LoginPartial() { if (Request.IsAjaxRequest()) { return View("_LoginPartial"); } return new EmptyResult(); }需要對原始項目模板進行最後一次修改。
_LoginPartial必須修改視圖以在沒有佈局視圖的情況下呈現:@{ Layout = null; }