web-dev-qa-db-ja.com

Asp.net MVC4:コントローラーとアクションの両方で認証

コントローラーとアクションの両方にAuthorize属性がある場合、どちらが有効になりますか?または、両方が有効になりますか?

69
frank

あなたが尋ねた:

コントローラーとアクションの両方にAuthorize属性がある場合、どちらが有効になりますか?両方?

これに単純に答えるには:両方。その効果は、2つの制限を一緒にANDすることです。以下にその理由を説明します...

詳細

そのため、これを尋ねる理由はいくつかあります。

  1. メソッドと比較して、アクションに追加の制約を適用する方法を知りたい。例えば。
    • コントローラーレベルで、ロール「ユーザー」のユーザーを強制する
    • アクションレベルで、ロール「admin」のユーザーをさらに強制する
  2. アクションレベルでコントローラーの制約を置き換えたい
  3. アクションレベルでコントローラーの制約を削除し、メソッドを匿名ユーザーが利用できるようにする場合

MVCバージョンを指定しなかったため、今日の最新バージョン(MVC 4.5)を想定します。ただし、MVC 3を使用していても、答えはあまり変わりません。

[Anonymous]はコントローラー[Authorize]をオーバーライドします(ケース3)

ケース3. SO全体 および Web全体 で既に回答されているため、([AllowAnonymous]の使用)をカバーする必要はありません。言うだけで十分です:アクションで[AllowAnonymous]を指定すると、コントローラーの[Authorize]があっても、そのアクションがパブリックになります。

グローバルフィルターを使用 でWebサイト全体を承認の対象にし、公開するいくつかのアクションまたはコントローラーでAllowAnonymousを使用することもできます。

[Authorize]は加算的です(ケース1)

ケース1は簡単です。例として次のコントローラーを取り上げます。

[Authorize(Roles="user")]
public class HomeController : Controller {
    public ActionResult AllUsersIndex() {
        return View();
    }

    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
}

デフォルトでは、[Authorize(Roles="user")]は、コントローラーのすべてのアクションを「ユーザー」ロールのアカウントのみが使用できるようにします。したがって、AllUsersIndexにアクセスするには、「ユーザー」ロールに属している必要があります。ただし、AdminUsersIndexにアクセスするには、「user」ロールと「admin」ロールの両方である必要があります。例えば:

  • ユーザー名:Bob、ロール:ユーザー、cannotAdminUsersIndexにアクセスできますが、AllUsersIndexにはアクセスできます
  • ユーザー名:ジェーン、ロール:管理者、cannotAdminUsersIndexまたはAllUsersIndexにアクセス
  • ユーザー名:Tim、ロール:ユーザーおよび管理者、canアクセスAdminUsersIndexおよびAllUsersIndex

これは、[Authorize]属性が付加的であることを示しています。これは、属性の Users プロパティにも当てはまります。これをRolesと組み合わせて、さらに制限することができます。

この動作は、コントローラーとアクションの属性の動作方法によるものです。属性はチェーン化され、オーダーコントローラー、アクションに適用されます。最初のものが許可を拒否すると、制御が戻り、アクションの属性は呼び出されません。最初のものが認証に合格すると、2番目のものも同様にチェックされます。 Order (たとえば、[Authorize(Roles = "user", Order = 2)])を指定することにより、この順序をオーバーライドできます。

[Authorize]のオーバーライド(ケース2)

ケース2は複雑です。上記から、[Authorize]属性が(グローバル、次に)コントローラー、アクションの順に検査されることを思い出してください。ユーザーが許可される資格がないことを最初に検出したユーザーが勝ち、他のユーザーは呼び出されません。

これを回避する1つの方法は、次の2つの新しい属性を定義することです。 [OverrideAuthorize]は、[Authorize]に従うだけです。その唯一の目的は、チェックできる型を定義することです。 [DefaultAuthorize]により、リクエストで呼び出されるActionが[OverrideAuthorize]で装飾されているかどうかを確認できます。そうである場合は、アクション認証チェックに従います。それ以外の場合は、コントローラーレベルのチェックに進みます。

public class DefaultAuthorizeAttribute : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var action = filterContext.ActionDescriptor;
        if (action.IsDefined(typeof(OverrideAuthorizeAttribute), true)) return;

        base.OnAuthorization(filterContext);
    }
}
public class OverrideAuthorizeAttribute : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
    }
}

その後、次のように使用できます。

[DefaultAuthorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}

上記の例では、SuperusersIndexは、「スーパーユーザー」ロールを持つアカウントが、「ユーザー」ロールがない場合でも使用できます。

147
Andy Brown

[Authorize]のオーバーライド(ケース2)に何かを追加したい

OverrideAuthorizeAttributeとDefaultAuthorizeAttributeは正常に機能しますが、上位レベルで定義された承認フィルターをオーバーライドするOverrideAuthorizationAttributeも使用できることを発見しました。

[Authorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorization()]
    [Authorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}
43
Akodo_Shado

ASP.NET Core 2.1用に この回答の2番目のケース を変更しました。

ASP.NET CoreのAuthorizeAttributeとの違いは、通常の認証に進むためにAuthorizeAttribute.OnAuthorization基本メソッドを呼び出す必要がないことです。つまり、ベースメソッドを明示的に呼び出さなくても、ベースAuthorizeAttributeはアクセスを禁止することにより、許可を短絡させる可能性があります。

私がしたことは、DefaultAuthorizeAttributeからではなく、AuthorizeAttributeから継承するAttributeを作成したことです。 DefaultAuthorizeAttributeAuthorizeAttributeから継承しないため、認証動作を再作成する必要がありました。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class DefaultAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    private readonly AuthorizeFilter m_authorizeFilter;

    public DefaultAuthorizeAttribute(params string[] authenticationSchemes)
    {
        var policyBuilder = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(authenticationSchemes)
            .RequireAuthenticatedUser();
        m_authorizeFilter = new AuthorizeFilter(policyBuilder.Build());
    }

    public void OnAuthorization(AuthorizationFilterContext filterContext)
    {
        if (filterContext.ActionDescriptor is ControllerActionDescriptor controllerAction
            && controllerAction.MethodInfo.GetCustomAttributes(typeof(OverrideAuthorizeAttribute), true).Any())
        {
            return;
        }
        m_authorizeFilter.OnAuthorizationAsync(filterContext).Wait();
    }
}

public class OverrideAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext filterContext) { }
}
2
Jo Ham

コントローラーで使用すると、このコントローラーのすべてのメソッドが影響を受けます。

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

これらのアクションのいずれかを防止する場合は、次のようなものを使用できます。

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

    [AllowAnonymous]
    public ActionResult Action3 // only this method is not effected...
0