ASP.NET Core 1.0(MVC 6)Webアプリで承認を設定しようとしています。
より制限的なアプローチ-デフォルトでは、すべてのコントローラーとアクションメソッドをAdmin
ロールを持つユーザーに制限します。そのため、次のようなグローバル認証属性を追加しています。
AuthorizationPolicy requireAdminRole = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireRole("Admin")
.Build();
services.AddMvc(options => { options.Filters.Add(new AuthorizeFilter(requireAdminRole));});
次に、特定の役割を持つユーザーが具体的なコントローラーにアクセスできるようにします。例えば:
[Authorize(Roles="Admin,UserManager")]
public class UserControler : Controller{}
「グローバルフィルター」はUserManager
が「管理者」ではないのでコントローラーにアクセスすることを許可しないため、もちろん機能しません。
MVC5では、カスタム認証属性を作成し、そこにロジックを配置することでこれを実装できました。次に、このカスタム属性をグローバルとして使用します。例えば:
public class IsAdminOrAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
ActionDescriptor action = filterContext.ActionDescriptor;
if (action.IsDefined(typeof(AuthorizeAttribute), true) ||
action.ControllerDescriptor.IsDefined(typeof(AuthorizeAttribute), true))
{
return;
}
base.OnAuthorization(filterContext);
}
}
カスタムAuthorizeFilter
を作成しようとしましたが、成功しませんでした。 APIは異なるようです。
だから私の質問は次のとおりです。デフォルトのポリシーを設定してから、特定のコントローラーとアクションに対してそれをオーバーライドすることは可能ですか?または似たようなもの。これで行きたくない
[Authorize(Roles="Admin,[OtherRoles]")]
これは潜在的なセキュリティ問題であるため、すべてのコントローラー/アクションで。誤ってAdmin
ロールを配置するのを忘れるとどうなりますか。
グローバルポリシーは、特定のコントローラーとアクションに適用するポリシーよりも制限が厳しいため、フレームワークを少し使用する必要があります。
UsersController
にアクセスするなど)既にお知らせしたように、グローバルフィルターを使用すると、Adminユーザーのみがコントローラーにアクセスできるようになります。 UsersController
に追加の属性を追加すると、bothAdminandUserManagerがアクセスできます。
MVC 5のアプローチと同様のアプローチを使用することもできますが、動作は異なります。
[Authorize]
属性に認証ロジックが含まれていません。AuthorizeFilter
は、ポリシーが満たされていることを確認するために許可サービスを呼び出すOnAuthorizeAsync
メソッドを持つものです。IApplicationModelProvider
を使用して、[Authorize]
属性を持つすべてのコントローラーとアクションにAuthorizeFilter
を追加します。1つのオプションはIsAdminOrAuthorizeAttribute
を再作成することですが、今回はAuthorizeFilter
として作成し、グローバルフィルタとして追加します。
public class IsAdminOrAuthorizeFilter : AuthorizeFilter
{
public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext context)
{
// If there is another authorize filter, do nothing
if (context.Filters.Any(item => item is IAsyncAuthorizationFilter && item != this))
{
return Task.FromResult(0);
}
//Otherwise apply this policy
return base.OnAuthorizationAsync(context);
}
}
services.AddMvc(opts =>
{
opts.Filters.Add(new IsAdminOrAuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("admin").Build()));
});
これは、コントローラー/アクションに特定の[Authorize]
属性がない場合にのみ、グローバルフィルターを適用します。
また、すべてのコントローラーとアクションに適用されるフィルターを生成するプロセスに自分自身を挿入することにより、グローバルフィルターを使用しないようにすることもできます。独自のIApplicationModelProvider
または独自のIApplicationModelConvention
を追加できます。両方とも、特定のコントローラーおよびアクションフィルターを追加/削除できます。
たとえば、デフォルトの許可ポリシーと追加の特定のポリシーを定義できます。
services.AddAuthorization(opts =>
{
opts.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole("admin").Build();
opts.AddPolicy("Users", policy => policy.RequireAuthenticatedUser().RequireRole("admin", "users"));
});
次に、新しいIApplicatioModelProvider
を作成して、独自の[Authorize]
属性を持たないすべてのコントローラーにデフォルトポリシーを追加します(アプリケーションの規則は非常によく似ており、おそらくフレームワークは拡張されることを意図しています。私はすぐに既存のAuthorizationApplicationModelProvider
をガイドとして使用しました):
public class OverridableDefaultAuthorizationApplicationModelProvider : IApplicationModelProvider
{
private readonly AuthorizationOptions _authorizationOptions;
public OverridableDefaultAuthorizationApplicationModelProvider(IOptions<AuthorizationOptions> authorizationOptionsAccessor)
{
_authorizationOptions = authorizationOptionsAccessor.Value;
}
public int Order
{
//It will be executed after AuthorizationApplicationModelProvider, which has order -990
get { return 0; }
}
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
foreach (var controllerModel in context.Result.Controllers)
{
if (controllerModel.Filters.OfType<IAsyncAuthorizationFilter>().FirstOrDefault() == null)
{
//default policy only used when there is no authorize filter in the controller
controllerModel.Filters.Add(new AuthorizeFilter(_authorizationOptions.DefaultPolicy));
}
}
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
//empty
}
}
//Register in Startup.ConfigureServices
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, OverridableDefaultAuthorizationApplicationModelProvider>());
これを設定すると、これらの2つのコントローラーでデフォルトポリシーが使用されます。
public class FooController : Controller
[Authorize]
public class BarController : Controller
ここでは、特定のユーザーポリシーが使用されます。
[Authorize(Policy = "Users")]
public class UsersController : Controller
管理者ロールをすべてのポリシーに追加する必要がありますが、少なくともすべてのポリシーが単一の起動方法で宣言されることに注意してください。おそらく管理者ロールを常に追加するポリシーを作成する独自のメソッドを作成できます。
@Danielのソリューションを使用して、コメントで@TarkaDaalが言及した同じ問題に遭遇しました(各呼び出しのコンテキストには2 AuthorizeFilter
があります...それらがどこから来たのかはよくわかりません)。
だからそれを解決する私の方法は次のとおりです:
_public class IsAdminOrAuthorizeFilter : AuthorizeFilter
{
public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
{
if (context.Filters.Any(f =>
{
var filter = f as AuthorizeFilter;
//There's 2 default Authorize filter in the context for some reason...so we need to filter out the empty ones
return filter?.AuthorizeData != null && filter.AuthorizeData.Any() && f != this;
}))
{
return Task.FromResult(0);
}
//Otherwise apply this policy
return base.OnAuthorizationAsync(context);
}
}
services.AddMvc(opts =>
{
opts.Filters.Add(new IsAdminOrAuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("admin").Build()));
});
_
これはいですが、引数なしでAuthorize属性のみを使用している場合は、とにかくnew AuthorizationPolicyBuilder().RequireRole("admin").Build()
フィルターによって処理されるため、このケースでは機能します。