web-dev-qa-db-ja.com

メンバーシッププロバイダーを使用したASP.NET MVC 4 Web API認証

Web APIを使用するASP.NET MVC 4プロジェクトがあります。コントローラーで、[Authorize]属性を使用して承認を要求するようにクラスを設定しました。認証には、ASP.NETメンバーシッププロバイダーを使用しており、Web.Configで "フォーム"認証を使用するように設定しています。ここで私が立ち往生しています:

APIのテストが完了し、[Authorize]属性でコントローラーを保護して、メンバーシッププロバイダーのユーザーに対する認証のテストを開始できるようになるまで、すべてがうまく機能しています。そこで、Fiddlerを起動し、メンバーシッププロバイダーからのusername:passwordと共にAuthorization:Basic属性を追加して同じ呼び出しを行います。

enter image description here

私が受け取る応答は401不正であり、「Auth」の下で「No WWW-Authenticate Header is present」が返されます。次に、APIがSHA1エンコードキーを探していることを認識します。そこで、検索からSHA1ジェネレーターを起動し、username:passwordのハッシュを取得し、次のように要求ヘッダーを更新します。

enter image description here

これも機能せず、同じ結果が得られます。また、サーバーでユーザー名/パスワードをデコードするために使用する「共有秘密キー」が必要です。

だから私の質問:

  1. サーバーからこのキーを取得するにはどうすればよいですか(この場合はVirtual IIS running VS 2012)。
  2. これを使用して、ASP.NETメンバーシッププロバイダーからのユーザー名/パスワードを使用してFiddlerで認証済み呼び出しを行うにはどうすればよいですか。
  3. クライアントアプリケーションでこれを使用して同じ呼び出しを行う方法(C#WPFアプリ)。
  4. これは、HTTP呼び出しでSSLと組み合わせたときに最も効果的ですか?そうでない場合は何ですか?

前もって感謝します!

43
INNVTV

SSLで basic authentication を使用できます。サーバー側では、登録したメンバーシッププロバイダーにクエリを実行して資格情報を検証し、有効な場合はロールを取得して現在のプリンシパルを設定するカスタム委任ハンドラーを作成できます。

public class BasicAuthenticationMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var authHeader = request.Headers.Authorization;

        if (authHeader == null)
        {
            return base.SendAsync(request, cancellationToken);
        }

        if (authHeader.Scheme != "Basic")
        {
            return base.SendAsync(request, cancellationToken);
        }

        var encodedUserPass = authHeader.Parameter.Trim();
        var userPass = Encoding.ASCII.GetString(Convert.FromBase64String(encodedUserPass));
        var parts = userPass.Split(":".ToCharArray());
        var username = parts[0];
        var password = parts[1];

        if (!Membership.ValidateUser(username, password))
        {
            return base.SendAsync(request, cancellationToken);
        }

        var identity = new GenericIdentity(username, "Basic");
        string[] roles = Roles.Provider.GetRolesForUser(username);
        var principal = new GenericPrincipal(identity, roles);
        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }

        return base.SendAsync(request, cancellationToken);
    }
}

次に、このハンドラーをApplication_Startに登録します。

GlobalConfiguration.Configuration.MessageHandlers.Add(
    new BasicAuthenticationMessageHandler()
);

これで、[Authorize]属性で装飾され、認証されたユーザーのみがそのアクションにアクセスできるようにするApiコントローラーを作成できました。

[Authorize]
public class ValuesController : ApiController
{
    public string Get()
    {
        return string.Format("Hello {0}", User.Identity.Name);
    }
}

では、サンプルクライアントを見てみましょう。

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

class Program
{
    static void Main()
    {
        // since for testing purposes I am using IIS Express
        // with an invalid SSL certificate I need to desactivate
        // the check for this certificate.
        ServicePointManager.ServerCertificateValidationCallback += 
            (sender, certificate, chain, sslPolicyErrors) => true;

        using (var client = new HttpClient())
        {
            var buffer = Encoding.ASCII.GetBytes("john:secret");
            var authHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(buffer));
            client.DefaultRequestHeaders.Authorization = authHeader;
            var task = client.GetAsync("https://localhost:44300/api/values");
            if (task.Result.StatusCode == HttpStatusCode.Unauthorized)
            {
                Console.WriteLine("wrong credentials");
            }
            else
            {
                task.Result.EnsureSuccessStatusCode();
                Console.WriteLine(task.Result.Content.ReadAsAsync<string>().Result);
            }
        }
    }
}
68
Darin Dimitrov