9

ASP.Net MVC 4 の新しい OAuthWebSecurity 機能の使用方法を理解しようとしています。facebook または twitter の外部ログイン ボタンをクリックしたときに、現在のページを更新する代わりにフォームをポップアップに投稿することは可能ですか? Javascript を使用する前に Twitter と Facebook で oauth を使用しましたが、外部認証はポップアップで行われます。結果が非同期的に返された後、ポップアップは閉じます。MVC 4 の新しい OAuthWebSecurity 機能を使用して、これと同様のことを行うことはできますか? ありがとう。

4

1 に答える 1

29

この問題を解決するには、いくつかの側面があります。

  1. 認証シーケンスに対応するポップアップを開きます。
  2. 認証が完了したらポップアップを閉じます。
  3. 認証失敗の処理。
  4. ユーザーが認証されているという事実を反映するように親ページを更新します。

MVC4Internet 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 アカウントを 1 つの 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 に。

ただし、認証がユーザーによって明示的に開始された場合 (たとえば、ログイン ページにアクセスすることによって)、親ページはリダイレクトされないため、ユーザーが現在ログインしているという事実を反映するために、部分的なページの更新が必要になる場合があります。LogoutMVC テンプレート サンプルでは、​​ユーザーの名前とボタンの代わりにボタンを表示するようにページを更新する必要が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>

_LoginPartial上記の JavaScript は、既存のビューをレンダリングして返す新しいアクション メソッドへの AJAX 呼び出しを行います。

[HttpGet]
public ActionResult LoginPartial()
{
    if (Request.IsAjaxRequest())
    {
        return View("_LoginPartial");
    }

    return new EmptyResult();
}

元のプロジェクト テンプレートを最後に 1 つ変更する必要があります。_LoginPartialレイアウト ビューなしでレンダリングするには、ビューを変更する必要があります。

@{
    Layout = null;
}
于 2012-10-30T13:05:49.330 に答える