C# 将AuthorizationPolicy绑定到控制器/操作,而不使用AuthorizationAttribute
我想向.NET核心API添加授权。假设我有一个PersonController,具有以下操作:C# 将AuthorizationPolicy绑定到控制器/操作,而不使用AuthorizationAttribute,c#,asp.net-core,authorization,authorize-attribute,C#,Asp.net Core,Authorization,Authorize Attribute,我想向.NET核心API添加授权。假设我有一个PersonController,具有以下操作: GetPerson(根据id检索人员) PostPerson(添加一个新人) DeletePerson(删除一个人) 但现在我想将这些策略绑定到控制器操作,以说明执行控制器操作需要哪些策略。我知道这可以通过下面这样的authorizationAttribute来完成:[authorization(Policy=“X”],但我希望能够在不使用authorizationAttribute的情况下完成
- GetPerson(根据id检索人员)
- PostPerson(添加一个新人)
- DeletePerson(删除一个人)
但现在我想将这些策略绑定到控制器操作,以说明执行控制器操作需要哪些策略。我知道这可以通过下面这样的authorizationAttribute来完成:
,但我希望能够在不使用authorizationAttribute的情况下完成 为什么我不能使用[授权]属性?[authorization(Policy=“X”]
我不会说得太详细,但是控制器的源代码已经生成。这意味着所有手动更改都将在再次生成后被覆盖。因此,授权不应该在控制器中 在startup.cs中,我将控制器映射到端点,如下所示:app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
endpoints.MapControllers().RequireAuthorization("SuperAdmin");
// pseudo-code // endpoints.MapControllerAction("GetPerson").RequireAuthorization("SuperAdmin", "PersonReader");
public static class AuthorizeAttributeInjectionMvcOptionsExtensions { public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, params AuthorizeAttribute[] authorizeAttributes) { return options.ApplyAuthorizeAttributes(controllerName, null, authorizeAttributes); } public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, string actionName, params AuthorizeAttribute[] authorizeAttributes) { options.Conventions.Add(new AuthorizeAttributeInjectingConvention(controllerName, actionName, authorizeAttributes)); return options; } public static MvcOptions ApplyAuthorizationPolicy(this MvcOptions options, string controllerName, string actionName, params string[] policies) { return options.ApplyAuthorizeAttributes(controllerName, actionName, policies.Select(e => new AuthorizeAttribute(e)).ToArray()); } }
可以为所有控制器绑定一个策略,如下所示:services.AddMvc(o => { //... //by AuthorizeAttribute var withSuperAdminAttr = new AuthorizeAttribute("SuperAdmin"); o.ApplyAuthorizeAttributes("your_controller", "your_action", withSuperAdminAttr); //by policy o.ApplyAuthorizationPolicy("your_controller", "your_action", "SuperAdmin"); //... });
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
endpoints.MapControllers().RequireAuthorization("SuperAdmin");
// pseudo-code // endpoints.MapControllerAction("GetPerson").RequireAuthorization("SuperAdmin", "PersonReader");
public static class AuthorizeAttributeInjectionMvcOptionsExtensions { public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, params AuthorizeAttribute[] authorizeAttributes) { return options.ApplyAuthorizeAttributes(controllerName, null, authorizeAttributes); } public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, string actionName, params AuthorizeAttribute[] authorizeAttributes) { options.Conventions.Add(new AuthorizeAttributeInjectingConvention(controllerName, actionName, authorizeAttributes)); return options; } public static MvcOptions ApplyAuthorizationPolicy(this MvcOptions options, string controllerName, string actionName, params string[] policies) { return options.ApplyAuthorizeAttributes(controllerName, actionName, policies.Select(e => new AuthorizeAttribute(e)).ToArray()); } }
但这意味着我将要求所有控制器操作使用“超级管理员”策略。因此,我无法定义特定操作所需的策略。我希望这样做:services.AddMvc(o => { //... //by AuthorizeAttribute var withSuperAdminAttr = new AuthorizeAttribute("SuperAdmin"); o.ApplyAuthorizeAttributes("your_controller", "your_action", withSuperAdminAttr); //by policy o.ApplyAuthorizationPolicy("your_controller", "your_action", "SuperAdmin"); //... });
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
endpoints.MapControllers().RequireAuthorization("SuperAdmin");
// pseudo-code // endpoints.MapControllerAction("GetPerson").RequireAuthorization("SuperAdmin", "PersonReader");
public static class AuthorizeAttributeInjectionMvcOptionsExtensions { public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, params AuthorizeAttribute[] authorizeAttributes) { return options.ApplyAuthorizeAttributes(controllerName, null, authorizeAttributes); } public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, string actionName, params AuthorizeAttribute[] authorizeAttributes) { options.Conventions.Add(new AuthorizeAttributeInjectingConvention(controllerName, actionName, authorizeAttributes)); return options; } public static MvcOptions ApplyAuthorizationPolicy(this MvcOptions options, string controllerName, string actionName, params string[] policies) { return options.ApplyAuthorizeAttributes(controllerName, actionName, policies.Select(e => new AuthorizeAttribute(e)).ToArray()); } }
services.AddMvc(o => { //... //by AuthorizeAttribute var withSuperAdminAttr = new AuthorizeAttribute("SuperAdmin"); o.ApplyAuthorizeAttributes("your_controller", "your_action", withSuperAdminAttr); //by policy o.ApplyAuthorizationPolicy("your_controller", "your_action", "SuperAdmin"); //... });
很遗憾,我找不到任何方法来执行此操作。是否有方法可以在不使用[Authorize]的情况下将策略绑定到控制器操作属性?您可以通过应用程序模型约定
以编程方式应用iaapplicationmodelconvention
或任何其他类型的属性。在那里,您可以访问根AuthorizeAttribute
,其中包含所有加载的控制器,并且可以添加ApplicationModel
在那里。每个控制器都由一个名为AuthorizeAttribute
的类表示。它实现了ControllerModel
,该类公开了IFilterModel
的列表。该模型还实现了IFilterMetadata
,该类公开了属性列表,但该列表是只读的。因此,要修改该列表,您可能需要创建一个新模型来覆盖旧模型,这相当复杂。每个操作都由ICommonModel
表示,它还实现了ActionModel
。因此在这种情况下,我们不尝试通过将IFilterModel
添加到属性列表来应用它,而是将其转换为AuthorizeAttribute
也是一个AuthorizeFilter
,因此可以将其添加到IFilterMetadata
公开的筛选器列表中 详细代码如下:IFilterModel
现在在public class AuthorizeAttributeInjectingConvention : IApplicationModelConvention { readonly string _controller; readonly string _action; readonly AuthorizeFilter[] _authorizeFilters; public AuthorizeAttributeInjectingConvention(string controllerName, params AuthorizeAttribute[] authorizeAttributes) : this(controllerName, null, authorizeAttributes) { } public AuthorizeAttributeInjectingConvention(string controllerName, string actionName, params AuthorizeAttribute[] authorizeAttributes) { _controller = controllerName; _action = actionName; _authorizeFilters = authorizeAttributes.Select(e => new AuthorizeFilter(new[] { e })).ToArray(); } public void Apply(ApplicationModel application) { var filterModels = application.Controllers .Where(e => string.Equals(e.ControllerName, _controller, StringComparison.OrdinalIgnoreCase)) .ToList<IFilterModel>(); if(filterModels.Count > 0 && !string.IsNullOrWhiteSpace(_action)) { filterModels = filterModels.Cast<ControllerModel>() .SelectMany(e => e.Actions.Where(o => string.Equals(o.ActionName, _action, StringComparison.OrdinalIgnoreCase))) .ToList<IFilterModel>(); } foreach(var filterModel in filterModels) { foreach(var af in _authorizeFilters) { filterModel.Filters.Add(af); } } } }
中,您可以将所选的Startup.ConfigureServices
应用于特定的控制器或操作(通过其名称),如下所示:authorized属性
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
endpoints.MapControllers().RequireAuthorization("SuperAdmin");
// pseudo-code // endpoints.MapControllerAction("GetPerson").RequireAuthorization("SuperAdmin", "PersonReader");
public static class AuthorizeAttributeInjectionMvcOptionsExtensions { public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, params AuthorizeAttribute[] authorizeAttributes) { return options.ApplyAuthorizeAttributes(controllerName, null, authorizeAttributes); } public static MvcOptions ApplyAuthorizeAttributes(this MvcOptions options, string controllerName, string actionName, params AuthorizeAttribute[] authorizeAttributes) { options.Conventions.Add(new AuthorizeAttributeInjectingConvention(controllerName, actionName, authorizeAttributes)); return options; } public static MvcOptions ApplyAuthorizationPolicy(this MvcOptions options, string controllerName, string actionName, params string[] policies) { return options.ApplyAuthorizeAttributes(controllerName, actionName, policies.Select(e => new AuthorizeAttribute(e)).ToArray()); } }
请注意,上面的代码并不完美,它介绍了如何基本上实现它。您可以进一步改进的逻辑是如何筛选目标控制器和操作。在我的示例中,它只是根据控制器名称和操作名称进行筛选。我认为,只要您有唯一的控制器名称和uniqu,这几乎在任何情况下都会起作用e操作名称。否则,在实际应用services.AddMvc(o => { //... //by AuthorizeAttribute var withSuperAdminAttr = new AuthorizeAttribute("SuperAdmin"); o.ApplyAuthorizeAttributes("your_controller", "your_action", withSuperAdminAttr); //by policy o.ApplyAuthorizationPolicy("your_controller", "your_action", "SuperAdmin"); //... });
authorized属性之前,您可能必须添加更多自定义逻辑以针对正确的控制器和操作