37

System.Web.Http.AuthorizeAttributeから継承して、カスタム承認/認証ルーチンを作成し、ASP.NET MVC 4 を使用して開発された Web アプリケーションのいくつかの異常な要件を満たすようにしています。これにより、Web からの Ajax 呼び出しに使用される Web API にセキュリティが追加されます。クライアント。要件は次のとおりです。

  1. ユーザーは、トランザクションを実行するたびにログオンして、誰かがログオンして立ち去った後、他の誰かがワークステーションに近づいていないことを確認する必要があります。
  2. プログラム時にロールを Web サービス メソッドに割り当てることはできません。管理者がこれを構成できるように、実行時に割り当てる必要があります。この情報は、システム データベースに保存されます。

Web クライアントはシングル ページ アプリケーション (SPA)であるため、通常のフォーム認証はうまく機能しませんが、要件を満たすために ASP.NET セキュリティ フレームワークをできる限り再利用しようとしています。カスタマイズされたAuthorizeAttributeは、Web サービス メソッドに関連付けられているロールを決定する要件 2 に最適です。アプリケーション名、リソース名、および操作の 3 つのパラメーターを受け入れて、どのロールがメソッドに関連付けられているかを判断します。

public class DoThisController : ApiController
{
    [Authorize(Application = "MyApp", Resource = "DoThis", Operation = "read")]
    public string GetData()
    {
        return "We did this.";
    }
}

OnAuthorizationメソッドをオーバーライドして、ロールを取得し、ユーザーを認証します。ユーザーはトランザクションごとに認証を受ける必要があるため、認証と承認を同じ手順で実行することで、やり取りを減らすことができます。HTTP ヘッダーで暗号化された資格情報を渡す基本認証を使用して、Web クライアントからユーザーの資格情報を取得します。したがって、私のOnAuthorizationメソッドは次のようになります。

public override void OnAuthorization(HttpActionContext actionContext)
{

     string username;
     string password;
     if (GetUserNameAndPassword(actionContext, out username, out password))
     {
         if (Membership.ValidateUser(username, password))
         {
             FormsAuthentication.SetAuthCookie(username, false);
             base.Roles = GetResourceOperationRoles();
         }
         else
         {
             FormsAuthentication.SignOut();
             base.Roles = "";
         }
     }
     else
     {
         FormsAuthentication.SignOut();
         base.Roles = "";
     }
     base.OnAuthorization(actionContext);
 }

GetUserNameAndPasswordは、HTTP ヘッダーから資格情報を取得します。次に、Membership.ValidateUserを使用して資格情報を検証します。カスタム データベースにアクセスするために、カスタム メンバーシップ プロバイダーとロール プロバイダーをプラグインしています。ユーザーが認証されたら、リソースと操作のロールを取得します。そこから、ベースのOnAuthorizationを使用して認証プロセスを完了します。ここが壊れるところです。

ユーザーが認証されている場合は、標準のフォーム認証方法を使用してユーザーをログインさせ (FormsAuthentication.SetAuthCookie)、失敗した場合はログアウトさせます (FormsAuthentication.SignOut)。しかし、問題は、ベースの OnAuthorizationクラスが、 IsAuthenticatedが正しい値に設定されるように更新されたプリンシパルにアクセスできないことです。いつも一歩後ろです。そして、Web クライアントへの往復があるまで更新されないキャッシュされた値を使用していると思います。

つまり、Cookie を使用せずにIsAuthenticatedを現在のプリンシパルの正しい値に設定する別の方法はありますか? 毎回認証する必要があるこの特定のシナリオでは、Cookie は実際には適用されないように思えます。IsAuthenticatedが正しい値に設定されていないことがわかっている理由は、 HandleUnauthorizedRequestメソッドもこれにオーバーライドしているためです。

 protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
 {
     if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated)
     {
         filterContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
     }
     else
     {
         base.HandleUnauthorizedRequest(filterContext);
     }
 }

これにより、失敗の原因が認証ではなく承認であった場合に、Forbidden のステータス コードを Web クライアントに返すことができ、それに応じて応答できます。

では、このシナリオで現在のプリンシパルにIsAuthenticatedを設定する適切な方法は何ですか?

4

3 に答える 3

40

私のシナリオの最善の解決策は、ベースのOnAuthorization を完全にバイパスすることです。Cookie とキャッシュのたびに認証を行う必要があるため、この原則はあまり役に立ちません。だからここに私が思いついた解決策があります:

public override void OnAuthorization(HttpActionContext actionContext)
{
    string username;
    string password;

    if (GetUserNameAndPassword(actionContext, out username, out password))
    {
        if (Membership.ValidateUser(username, password))
        {
            if (!isUserAuthorized(username))
                actionContext.Response = 
                    new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            actionContext.Response = 
                new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        }
    }
    else
    {
        actionContext.Response = 
            new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
    }
}

isUserAuthorizedと呼ばれるロールを検証するための独自の方法を開発しました。現在の原則をチェックしてisAuthenticatedかどうかを確認するため、ベースのOnAuthorizationはもう使用していません。 IsAuthenticatedは取得のみを許可するため、他に設定する方法がわかりません。現在のPrincipleは必要ないようです。これをテストしたところ、正常に動作します。

誰かがより良い解決策を持っているか、これに問題があるかどうかまだ興味があります.

于 2012-09-28T20:18:16.010 に答える
29

すでに受け入れられている回答に追加するには: Checking current sourcecode (aspnetwebstack.codeplex.com) for System.Web.Http.AuthorizeAttribute, ドキュメントが古くなっているようです. BaseOnAuthorization()は、private static を呼び出し/チェックSkipAuthorization()するだけです (これAllowAnonymousAttributeは、残りの認証チェックをバイパスするためにコンテキストで使用されているかどうかをチェックするだけです)。次に、スキップされない場合OnAuthorization()は publicIsAuthorized()を呼び出し、その呼び出しが失敗した場合は protected virtual を呼び出しますHandleUnauthorizedRequest()。そして、それだけです...

public override void OnAuthorization(HttpActionContext actionContext)
{
    if (actionContext == null)
    {
        throw Error.ArgumentNull("actionContext");
    }

    if (SkipAuthorization(actionContext))
    {
        return;
    }

    if (!IsAuthorized(actionContext))
    {
        HandleUnauthorizedRequest(actionContext);
    }
}

の内部を見るとIsAuthorized()、ここでロールとユーザーに対して Principle がチェックされます。したがって、IsAuthorized()代わりに上記のものでオーバーライドするOnAuthorization()のが方法です。繰り返しになりますが、401 応答と 403 応答をいつ返すかを決定するには、どちらかOnAuthorization()またはとにかくをオーバーライドする必要があります。HandleUnauthorizedRequest()

于 2012-11-17T19:59:12.197 に答える