7

次の要件でMVC4Webアプリケーションを実装しようとしています。

(a)認証されたユーザーにのみサービスを提供します。認証に関しては、MVCの最新の認証技術であり、独自のdbテーブルを定義できるという利点があり、OAuthサポートをすぐに利用でき、MVCとMVCの両方と簡単に統合できるため、単純なメンバーシップを使用したいと思います。 WebApi。

(b)モバイル/ JSクライアント用のWebApiを介していくつかのコア機能を公開します。これは、基本HTTP認証(+ SSL)を介して認証する必要があります。通常、さまざまなユーザーロールのAuthorize属性で装飾された、WebApiコントローラーへのjQueryAJAX呼び出しを使用するJSクライアントがあります。

(c)理想的には、混合環境では、二重認証を避けたいと思います。つまり、ユーザーが既にブラウザーを介して認証されており、WebApiコントローラーアクションへのJS呼び出しを意味するページにアクセスしている場合、(a)メカニズムは次のようになります。足りる。

したがって、(a)はデフォルトのMVCテンプレートでカバーされていますが、(b)はブラウザーを介さずに基本HTTP認証を必要とします。この目的のために、この投稿で見つけたようなDelegatingHandlerを作成する必要があります:http ://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers 。問題は、その実装には、受信したユーザー名とパスワードからIPrincipalを取得する何らかの方法が必要であり、WebSecurityクラスはこのためのメソッドを提供しないことです(ログインを除くが、承認の目的でログに記録されたユーザーを変更することは避けます、(c)のような潜在的な「混合」環境のためにも。ですから、私の唯一の選択肢は、単純なメンバーシップを放棄することだと思われます。誰かより良い提案がありますか?引用された投稿からの関連する(わずかに変更された)コードは次のとおりです。

public interface IPrincipalProvider
{
    IPrincipal GetPrincipal(string username, string password);
}

public sealed class Credentials
{
    public string Username { get; set; }
    public string Password { get; set; }
}

public class BasicAuthMessageHandler : DelegatingHandler
{
    private const string BasicAuthResponseHeader = "WWW-Authenticate";
    private const string BasicAuthResponseHeaderValue = "Basic";

    public IPrincipalProvider PrincipalProvider { get; private set; }

    public BasicAuthMessageHandler(IPrincipalProvider provider)
    {
        if (provider == null) throw new ArgumentNullException("provider");
        PrincipalProvider = provider;
    }

    private static Credentials ParseAuthorizationHeader(string sHeader)
    {
        string[] credentials = Encoding.ASCII.GetString(
            Convert.FromBase64String(sHeader)).Split(new[] { ':' });

        if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) ||
            String.IsNullOrEmpty(credentials[1])) return null;

        return new Credentials
        {
            Username = credentials[0],
            Password = credentials[1],
        };
    }

    protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        AuthenticationHeaderValue authValue = request.Headers.Authorization;
        if (authValue != null && !String.IsNullOrWhiteSpace(authValue.Parameter))
        {
            Credentials parsedCredentials = ParseAuthorizationHeader(authValue.Parameter);
            if (parsedCredentials != null)
            {
                Thread.CurrentPrincipal = PrincipalProvider
                    .GetPrincipal(parsedCredentials.Username, parsedCredentials.Password);
            } 
        } 

        return base.SendAsync(request, cancellationToken)
            .ContinueWith(task =>
            {
                var response = task.Result;
                if (response.StatusCode == HttpStatusCode.Unauthorized
                    && !response.Headers.Contains(BasicAuthResponseHeader))
                {
                    response.Headers.Add(BasicAuthResponseHeader,
                        BasicAuthResponseHeaderValue);
                } 
                return response;
            });
    }
}
4

3 に答える 3

3

ありがとう、これは現時点で利用可能な最良の解決策のようです!ダミーのソリューションを最初から作成することができました(ここで見つけてください:http ://sdrv.ms/YpkRcf )。次の場合に機能するようです。

1)MVCコントローラーの制限付きアクションにアクセスしようとすると、認証を受けるために期待どおりにログインページにリダイレクトされます。

2)WebApiコントローラーの制限付きアクションに対してjQuery ajax呼び出しをトリガーすると、呼び出しは成功します(もちろん、SSLを使用していない場合を除く)。

ただし、Webサイトにログインした後でも、API呼び出しで認証が必要な場合は機能しません。誰かがここで何が起こっているのか説明できますか?以下では、私のような初心者に役立つと思うので、手順を詳しく説明します。

ありがとうございます(以下のフォーマットについては申し訳ありませんが、このエディターにコードを適切にマークさせることはできません...)


手順

  1. 新しいmvc4アプリを作成します(基本的なmvc4アプリ:これにはすでにユニバーサルプロバイダーが付属しています。すべてのユニバーサルプロバイダーのクラス名はデフォルトで始まります...);

  2. 非ローカルDB用にweb.configをカスタマイズします。例:

      <connectionStrings>
    <add name="DefaultConnection"
     providerName="System.Data.SqlClient"
     connectionString="data source=(local)\SQLExpress;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True" />
    

また、パスワードをハッシュするためにmachineKeyを設定すると、パスワードをスクランブルせずにこのサイトをサーバー間で自由に移動できるので便利なことがよくあります。マシンキージェネレータのWebサイトを使用して、次のようなエントリを定義します。

  <system.web>
        <machineKey
   validationKey="...thekey..."
   decryptionKey="...thekey..."
   validation="SHA1"
   decryption="AES" />
  1. 必要に応じて、web.configの接続文字列に対応する新しい空のデータベースを作成します。次に、古き良き仲間のWSATを(VSプロジェクトメニューから)開始し、必要に応じてユーザーとロールを追加してセキュリティを構成します。

  2. 必要に応じて、インデックスアクションを使用してHomeControllerを追加します。これは、このテンプレートにコントローラーが存在しないため、コントローラーがないとWebアプリをテスト起動できないためです。

  3. NuGetからThinktecture.IdentityModel.45を追加し、お気に入りのすべてのNuGetパッケージを追加/更新します。これを書いている時点では、MSからの控えめなjquery検証はjQuery1.9以降と互換性がないことに注意してください。私はむしろhttp://plugins.jquery.com/winf.unobtrusive-ajax/を使用します。したがって、jquery.unobtrusive *を削除し、このライブラリ(winf.unobtrusive-ajaxとadditional-methodsで構成される)をバンドル(App_Start / BundleConfig.cs)に追加します。

  4. App_StartのWebApiConfig.csを、DefaultApiルート構成の後にコードを追加して変更します。

    public static class WebApiConfig {public static void Register(HttpConfiguration config){config.Routes.MapHttpRoute(name: "DefaultApi"、routeTemplate: "api / {controller} / {id}"、defaults:new {id = RouteParameter.Optional} );

        // added for Thinktecture
        var authConfig = new AuthenticationConfiguration
        {
            InheritHostClientIdentity = true,
            ClaimsAuthenticationManager = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager
        };
    
        // setup authentication against membership
        authConfig.AddBasicAuthentication((userName, password) => Membership.ValidateUser(userName, password));
    
        config.MessageHandlers.Add(new AuthenticationHandler(authConfig));
    }
    

    }

わかりやすくするために、APIコントローラーはControllers / Api /の下に配置されるため、このフォルダーを作成します。

  1. LoginModel.csをモデルに追加します。

    public class LoginModel{[必須][Display(Name = "UserName"、ResourceType = typeof(StringResources))] public string UserName {get; セットする; }

    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password", ResourceType = typeof(StringResources))]
    public string Password { get; set; }
    
    [Display(Name = "RememberMe", ResourceType = typeof(StringResources))]
    public bool RememberMe { get; set; }
    

    }

このモデルには、StringResources.resxリソース(コード生成あり)が必要です。私は通常、Assetsフォルダーの下に配置し、3つの文字列を属性で引用します。

  1. 次のように、ClaimsTransformer.csをソリューションルートに追加します。

    public class ClaimsTransformer:ClaimsAuthenticationManager {public override ClaimsPrincipal Authenticate(string resourceName、ClaimsPrincipalcomingPrincipal){if(!incomingPrincipal.Identity.IsAuthenticated){return base.Authenticate(resourceName、incomingPrincipal); }

        var name = incomingPrincipal.Identity.Name;
    
        return Principal.Create(
            "Custom", 
            new Claim(ClaimTypes.Name, name + " (transformed)"));
    }
    

    }

  2. Application_PostAuthenticateRequestをGlobal.asax.csに追加します。

    public class MvcApplication:HttpApplication {... protected void Application_PostAuthenticateRequest(){if(ClaimsPrincipal.Current.Identity.IsAuthenticated){var transform = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager; var newPrincipal = transform.Authenticate(string.Empty、ClaimsPrincipal.Current);

            Thread.CurrentPrincipal = newPrincipal;
            HttpContext.Current.User = newPrincipal;
        }
    }
    

    }

  3. web.config(YourAppNamespaceをアプリのルート名前空間に置き換えます):

    <configSections> <section name = "system.identityModel" type = "System.IdentityModel.Configuration.SystemIdentityModelSection、System.IdentityModel、Version = 4.0.0.0、Culture = neutral、PublicKeyToken = B77A5C561934E089"/>..。

  4. アカウントコントローラーの他のモデルをそれらのビューとともに追加します(リテラルではなく文字列リソース名を必要とする属性を使用して、よりローカライズしやすいバリアントに変更したい場合でも、MVC3アプリケーションテンプレートから派生させることができます)。

  5. ブラウザベースの認証をテストするには、[承認済み]アクションをコントローラ(HomeControllerなど)に追加して、アクセスしてみます。

  6. 基本HTTP認証をテストするには、次のようなコードをビュー(Home / Indexなど)に挿入します(トークン変数にユーザー名とパスワードを設定します)。

    ..。

    <p>テストコール

    $(function(){$( "#test")。click(function(){var token = "USERNAME:PASSWORD"; var hash = $ .base64.encode(token); var header = "Basic" + hash; console.log(ヘッダー);
            $.ajax({
                url: "/api/dummy",
                dataType: "json",
                beforeSend: function(xhr) {
                    xhr.setRequestHeader("Authorization", header);
                },
                success: function(data) {
                    alert(data);
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    alert(errorThrown);
                }
            });
        });
    });
    

これには、Base64をエンコード/デコードするためのjQueryプラグイン(jquery.base64.jsとそれに対応する縮小版)が必要です。

SSLを許可するには、次の手順に従います: http: //www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx(基本的に、WebプロジェクトのプロパティでSSLを有効にし、プロパティ値で指定されたポートに接続します)。

于 2013-02-12T23:31:01.577 に答える
3

すべての要件を満たす別のソリューションを次に示します。SimpleMemberhsipを使用し、MVC4アプリケーションでフォーム認証と基本認証を組み合わせます。承認もサポートできますが、Roleプロパティをnullのままにしておく必要はありません。

于 2013-02-14T13:53:59.443 に答える
0

多分これは役に立ちます-これはあなたのシナリオのように聞こえます:

http://leastprivilege.com/2012/10/23/mixing-mvc-forms-authentication-and-web-api-basic-authentication/ http://leastprivilege.com/2012/10/24/extensions-to- the-web-apimvc-formsbasic-auth-sample-claims-transformation-and-ajax /

于 2013-02-11T21:54:46.817 に答える