3

ユーザーの資格情報を使用して Web サービスを呼び出し、ユーザーの ID である "LogonTicket" を含む WCF 構造を返すことにより、MVC3 アプリケーションの基本認証をオーバーライドします。この LogonTicket は、「Web サービスへの呼び出しごとにユーザーを認証するために使用されます。

ここで、Web.config の defaultProvider を置き換えることでオーバーライドします。このオーバーライドされたプロバイダーで行うことは、ValidateUser() 関数をオーバーライドすることだけです。ここで、資格情報を使用して Web サービスを呼び出し、"LogonTicket" を返します。

これは、AccountController の LogOn() 関数であり、基本的にはテンプレートのベース コードです。

public ActionResult LogOn(LogOnModel model)
{
    string ReturnUrl = "";
    if (HttpContext.Request.UrlReferrer.Query.Length > 11)
    {
        ReturnUrl = Uri.UnescapeDataString(HttpContext.Request.UrlReferrer.Query.Substring(11));
    }
    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(ReturnUrl) && ReturnUrl.Length > 1 && ReturnUrl.StartsWith("/")
                && !ReturnUrl.StartsWith("//") && !ReturnUrl.StartsWith("/\\"))
            {
                return Redirect(ReturnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

    // If we got this far, something failed, redisplay form
    ViewBag.MainWebsite = MainWebsite;
    return View(model);
}

これは、新しいデフォルト プロバイダーからオーバーライドされた ValidateUser() 関数です。

public override bool ValidateUser(string username, string password)
{
    MyServiceClient mps = new MyServiceClient();
    string sha1password = HashCode(password);
    LogonInfo logonInfo = mps.GetLogonTicket(username, sha1password);
    if (logonInfo.LogonTicket != "" && logonInfo.LogonTicket != "0") 
    {
    // Authenticated so set session variables
        HttpContext.Current.Session["LogonTicket"] = logonInfo.LogonTicket;
        HttpContext.Current.Session["ParticipantID"] = logonInfo.ParticipantID;
        return true;
    }
    else
    {
        return false;
    }
}

2つの使用を組み合わせる方法がよくわからないので、私の質問は次のとおりです。

  1. OpenID と Facebook のログインを実装し、現在の認証方法を維持するにはどうすればよいですか?
  2. OpenID ユーザーを現在のユーザー DB 値にどのように「マップ」できますか? 彼らの情報を取得できるように、知っておく必要があります。メール アドレスを取得できることはわかっていますが、OpenID メールがサイトで記録に使用しているメールと異なる場合はどうなりますか?
  3. これを行う方法の例はどこにありますか?

私の質問を見てくれてありがとう。

4

2 に答える 2

3

複数のログオンの可能性 (カスタム アカウント、Google および Facebook) が必要なプロジェクトを実行しました。

最終的に、ASP.NET での認証は構成に完全に依存します。(あなたの場合は FormsAuthentication です)これは、基本的FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);にユーザーに関するすべてを決定し、これを設定する場所は制限されないことを意味します。

これで、MembershipProvider を使用して独自のカスタム アカウントを処理するという、基本的に最初と同じ実装ができました。openIds を容易にするために、ここで展開するだけです。Controllerログインの種類ごとにさまざまなアクションを展開する必要がありActionResult LogOn()ます (これで、たとえば を追加できますActionResult LogOnOpenId())。そのメソッド内では、基本的に同じコードを呼び出しますが、代わりにMembership.ValidateUser(model.UserName, model.Password)OpenId サービスを呼び出します。

dotnetopenauth を使用した Google の実装例を以下に示します。service メソッドは、基本的formsService.SignIn(userId.Value.ToString(), false);に which を呼び出しますFormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);(SecurityPrincipal に関していくつかのカスタム動作を行うだけですが、これは認証プロセスには影響しません)。新しいユーザーを受け取ると、新しいアカウントを作成することもわかります。質問のパート 2 を解決するために、別のログインを提供できる場合にマージできるプロファイルを実装しました。これにより、ユーザーは自分のアカウントを統合したままにして、好きなログイン方法を使用できます。

複数のサインオンに関する例については、良い例として StackExchange を参照した Tomas の回答を参照します。また、MVC4 と VS2012 をインストールして、[ファイル] > [新しいプロジェクト] を実行することをお勧めします。MVC の最新のデフォルト テンプレートには、カスタム ログインとともに openid 実装が含まれています。

Google openid の実装例:

コントローラーメソッド:

    public virtual ActionResult LoginGoogle(string returnUrl, string runAction)
    {
        using (var openId = new OpenIdRelyingParty())
        {
            IAuthenticationResponse response = openId.GetResponse();

            // If we have no response, start 
            if (response == null)
            {
                // Create a request and redirect the user 
                IAuthenticationRequest req = openId.CreateRequest(WellKnownProviders.Google);
                var fetch = new FetchRequest();
                fetch.Attributes.AddRequired(WellKnownAttributes.Name.First);
                fetch.Attributes.AddRequired(WellKnownAttributes.Name.Last);
                fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
                fetch.Attributes.AddRequired(WellKnownAttributes.Preferences.Language);
                req.AddExtension(fetch);

                req.RedirectToProvider();

                return null;
            }

            _service.ConnectViaGoogle(response, TempData);
    }

サービス方法:

    public void ConnectViaGoogle(IAuthenticationResponse response, TempDataDictionary tempData)
    {
        // We got a response - check it's valid and that it's me 
        if (response.Status == AuthenticationStatus.Authenticated)
        {
            var claim = response.GetExtension<FetchResponse>();
            Identifier googleUserId = response.ClaimedIdentifier;
            string email = string.Empty;
            string firstName = string.Empty;
            string lastName = string.Empty;
            string language = string.Empty;

            if (claim != null)
            {
                email = claim.GetAttributeValue(WellKnownAttributes.Contact.Email);
                firstName = claim.GetAttributeValue(WellKnownAttributes.Name.First);
                lastName = claim.GetAttributeValue(WellKnownAttributes.Name.Last);
                language = claim.GetAttributeValue(WellKnownAttributes.Preferences.Language);
            }

            //Search User with google UserId
            int? userId = _userBL.GetUserIdByGoogleSingleSignOnId(googleUserId);

            //if not exists -> Create
            if (!userId.HasValue)
            {
                _userBL.CreateGoogleUser(
                    googleUserId,
                    firstName,
                    lastName,
                    email,
                    language,
                    DBConstants.UserStatus.DefaultStatusId,
                    out userId);
            }

            if (userId.HasValue)
            {
                _userBL.UpdateLastLogon(userId.Value);
                var formsService = new FormsAuthenticationService();
                formsService.SignIn(userId.Value.ToString(), false);
                AfterLoginActions(tempData);
            }
        }
    }

質問やコメントはありますか?喜んで伺います。

于 2012-11-14T13:53:10.583 に答える
1
  1. 複数の認証方法を持つことは完全に可能であるべきです。IIS / ASP.net が気にするのは FormsAuthentication Cookie だけです。したがって、標準のユーザー名/パスワード認証用に 1 つのアクション セットがあり、OpenId 用に別のアクション セットがあります。これは、少なくとも私が 1 つのプロジェクトで行ったことです。
  2. 電子メール アドレスを提供する openId プロバイダーを信頼することさえできません。この問題の一般的な解決策は、ユーザーがログイン後に自分のアカウントに複数の OpenId 識別子 (URI) を添付できるようにすることです。これは、たとえば StackOverflow のしくみです。ユーザーが初めてシステムにアクセスする場合は、新しいアカウントを自動作成するか、ユーザーにサインアップ プロセスを強制することができます。
    上記のシステムに OpenId サポートを追加したとき、ユーザー名とパスワードを格納するために使用される既存のテーブル (users テーブル) がありました。users テーブルと多対 1 の関係を持つ新しいテーブルを追加し、これを使用して URI を格納しました。
  3. 前述のように、StackOverflow 自体は開始するのに適した場所であり、http://www.dotnetopenauth.net/プロジェクトには多くの良い例があります。私の知る限り、SO のソースは公開されておらず、dotnetopenauth プロジェクトを使用しています。
    これは抽象的かもしれませんが、このライブラリは、オープン ソース オーチャード CMS の openId (とりわけ) です: http://orchardopenauth.codeplex.com/

これがお役に立てば幸いですが、ご不明な点がございましたら、質問をさらに詳しく説明してください。

于 2012-11-14T09:56:43.617 に答える