System.Web.Http.AuthorizeAttributeを継承して、ASP.NET MVC 4を使用して開発されたWebアプリケーションの異常な要件を満たすカスタムの認証/認証ルーチンを作成します。これにより、Web APIにセキュリティが追加されます。 WebクライアントからのAjax呼び出しに使用されます。要件は次のとおりです。
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が正しい値に設定されるように更新されるPrincipalにアクセスできないということのようです。常に一歩遅れています。そして、私の推測では、Webクライアントへの往復があるまで更新されないキャッシュされた値を使用していると考えられます。
したがって、これらすべてが私の特定の質問につながります。つまり、Cookieを使用せずにIsAuthenticatedを現在のPrincipalの正しい値に設定する別の方法がありますか?毎回認証する必要があるこの特定のシナリオでは、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クライアントに返すことができます。
このシナリオで現在のPrincipleにIsAuthenticatedを設定する適切な方法は何ですか?
私のシナリオに最適なソリューションは、ベース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と呼ばれるロールを検証するための独自のメソッドを開発し、現在のPrincipleを確認するため、ベースOnAuthorizationを使用していませんそれがあればisAuthenticated。 IsAuthenticatedはgetのみを許可するため、他にどのように設定すればよいかわかりません。現在のPrincipleは必要ないようです。これをテストし、正常に動作します。
誰かがより良い解決策を持っているか、これに関する問題を見ることができるなら、まだ興味があります。
すでに受け入れられている回答に追加するには、現在のソースコード(aspnetwebstack.codeplex.com)でSystem.Web.Http.AuthorizeAttribute
を確認すると、ドキュメントが古くなっているように見えます。ベースOnAuthorization()
は、プライベートスタティックSkipAuthorization()
を呼び出し/チェックするだけです(コンテキストでAllowAnonymousAttribute
が残りの認証チェックをバイパスするために使用されるかどうかをチェックするだけです)。次に、スキップされない場合、OnAuthorization()
はpublic IsAuthorized()
を呼び出し、その呼び出しが失敗した場合、保護された仮想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()
を上にあるものでオーバーライドする方法があります。繰り返しますが、OnAuthorization()
またはHandleUnauthorizedRequest()
のいずれかをオーバーライドして、401レスポンスと403レスポンスのどちらを返すかを決定する必要があります。
Kevinの絶対的な正解に追加するために、フレームワーク(または他のコンシューマー)のダウンストリームコードが悪影響を受けないように、応答オブジェクトの既存の.NETフレームワークパスを活用するように少し変更することができます。予測できないいくつかの奇妙な特異性によって。
具体的には、次のコードを使用することを意味します。
actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, REQUEST_NOT_AUTHORIZED);
のではなく:
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
REQUEST_NOT_AUTHORIZED
は次のとおりです。
private const string REQUEST_NOT_AUTHORIZED = "Authorization has been denied for this request.";
.NETフレームワークのSRResources.RequestNotAuthorized
定義からstring
を引き出しました。
すばらしい答えケビン!基本クラスでOnAuthorization
を実行しても意味がなかったので、アプリケーションにカスタムのHTTPヘッダーを検証し、実際にはプリンシパルをチェックしたくないため、同じ方法で実装しました。じゃない.