授权中间件不识别通过 MVC 约定添加的策略
Authorization middleware doesn't recognize policies added through MVC conventions
这是一个 ASP.NET 核心 3.1 网络应用程序。我正在使用改编自 this blog post:
的代码向我的 ApiController
read/write 方法添加策略
public class DefaultApiAuthorizationConvention : IActionModelConvention
{
private readonly string _apiReadPolicyName;
private readonly string _apiWritePolicyName;
public DefaultApiAuthorizationConvention(string apiReadPolicyName = Constants.POLICY_API_READ, string apiWritePolicyName = Constants.POLICY_API_WRITE)
{
_apiReadPolicyName = apiReadPolicyName;
_apiWritePolicyName = apiWritePolicyName;
}
public void Apply(ActionModel action)
{
// only apply to API controllers
if (!action.Controller.Attributes.Any(a => a is ApiControllerAttribute))
return;
// Require API Write policy for POST/PUT/DELETE methods, and API Read policy for GET methods
if (action.Attributes.Any(a => a is HttpPostAttribute || a is HttpPutAttribute || a is HttpDeleteAttribute))
{
action.Filters.Add(new AuthorizeFilter(policy: _apiWritePolicyName));
}
else if (action.Attributes.Any(a => a is HttpGetAttribute))
{
action.Filters.Add(new AuthorizeFilter(policy: _apiReadPolicyName));
}
}
}
添加了约定并在 Startup.cs
中注册了策略,这一切都很好。我还定义了默认和后备策略。
services.AddMvc(options =>
{
options.Conventions.Add(new AppAuth.DefaultApiAuthorizationConvention());
});
services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireRole(WebConstants.ROLE_SITE_ADMIN, WebConstants.ROLE_SITE_BROWSER)
.Build();
// general API policy
options.AddPolicy(AppAuth.Constants.POLICY_API_READ, policy =>
policy.RequireAuthenticatedUser()
.RequireRole(WebConstants.ROLE_SITE_ADMIN, WebConstants.ROLE_API_READ));
options.AddPolicy(AppAuth.Constants.POLICY_API_WRITE, policy =>
policy.RequireAuthenticatedUser()
.RequireRole(WebConstants.ROLE_SITE_ADMIN, WebConstants.ROLE_API_WRITE));
// other policies omitted
});
控制器就像这样,控制器或方法上没有 [Authorize]
属性。
[ApiController]
[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> Get()
{
return Ok(await _service.Get());
}
}
问题是当根据策略属于 ROLE_API_READ
的用户调用 API 方法时,AuthorizationMiddleware 总是将他们标记为未授权。
查看中间件源码,发现:
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
我在我的项目中复制了一份class以便调试,当我在这里设置一个断点时,authorizeData
变成了一个空数组。那么我们检查 EndpointMetadataCollection class:
public IReadOnlyList<T> GetOrderedMetadata<T>() where T : class
{
if (_cache.TryGetValue(typeof(T), out var result))
{
return (T[])result;
}
return GetOrderedMetadataSlow<T>(); // private method that gets the metadata real-time
}
这个_cache
变量在构造函数中设置,包含IAuthorizeData
的空数组:
Metadata
对象本身包含约定添加的 AuthorizeFilter
:
我确实联系了博客作者,令人惊讶的是他似乎不是 Stack Overflow,但我想把它开放给更广泛的社区,看看我的选择是什么。
在此先感谢您的帮助。
过滤器与端点元数据不同。您需要编写一个约定来改变 ActionModel
:
上的 SelectorModel
public class DefaultApiAuthorizationConvention : IActionModelConvention
{
private readonly string _apiReadPolicyName;
private readonly string _apiWritePolicyName;
public DefaultApiAuthorizationConvention(string apiReadPolicyName = Constants.POLICY_API_READ, string apiWritePolicyName = Constants.POLICY_API_WRITE)
{
_apiReadPolicyName = apiReadPolicyName;
_apiWritePolicyName = apiWritePolicyName;
}
public void Apply(ActionModel action)
{
// only apply to API controllers
if (!action.Controller.Attributes.Any(a => a is ApiControllerAttribute))
return;
// Require API Write policy for POST/PUT/DELETE methods, and API Read policy for GET methods
if (action.Attributes.Any(a => a is HttpPostAttribute || a is HttpPutAttribute || a is HttpDeleteAttribute))
{
foreach (var s in action.Selectors)
{
s.EndpointMetadata.Add(new AuthorizeAttribute(_apiWritePolicyName));
}
}
else if (action.Attributes.Any(a => a is HttpGetAttribute))
{
foreach (var s in action.Selectors)
{
s.EndpointMetadata.Add(new AuthorizeAttribute(_apiReadPolicyName));
}
}
}
PS:Auth 在 ASP.NET Core 3.0 中被重写为基于端点元数据,默认情况下不再使用 MVC 过滤器。
这是一个 ASP.NET 核心 3.1 网络应用程序。我正在使用改编自 this blog post:
的代码向我的ApiController
read/write 方法添加策略
public class DefaultApiAuthorizationConvention : IActionModelConvention
{
private readonly string _apiReadPolicyName;
private readonly string _apiWritePolicyName;
public DefaultApiAuthorizationConvention(string apiReadPolicyName = Constants.POLICY_API_READ, string apiWritePolicyName = Constants.POLICY_API_WRITE)
{
_apiReadPolicyName = apiReadPolicyName;
_apiWritePolicyName = apiWritePolicyName;
}
public void Apply(ActionModel action)
{
// only apply to API controllers
if (!action.Controller.Attributes.Any(a => a is ApiControllerAttribute))
return;
// Require API Write policy for POST/PUT/DELETE methods, and API Read policy for GET methods
if (action.Attributes.Any(a => a is HttpPostAttribute || a is HttpPutAttribute || a is HttpDeleteAttribute))
{
action.Filters.Add(new AuthorizeFilter(policy: _apiWritePolicyName));
}
else if (action.Attributes.Any(a => a is HttpGetAttribute))
{
action.Filters.Add(new AuthorizeFilter(policy: _apiReadPolicyName));
}
}
}
添加了约定并在 Startup.cs
中注册了策略,这一切都很好。我还定义了默认和后备策略。
services.AddMvc(options =>
{
options.Conventions.Add(new AppAuth.DefaultApiAuthorizationConvention());
});
services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireRole(WebConstants.ROLE_SITE_ADMIN, WebConstants.ROLE_SITE_BROWSER)
.Build();
// general API policy
options.AddPolicy(AppAuth.Constants.POLICY_API_READ, policy =>
policy.RequireAuthenticatedUser()
.RequireRole(WebConstants.ROLE_SITE_ADMIN, WebConstants.ROLE_API_READ));
options.AddPolicy(AppAuth.Constants.POLICY_API_WRITE, policy =>
policy.RequireAuthenticatedUser()
.RequireRole(WebConstants.ROLE_SITE_ADMIN, WebConstants.ROLE_API_WRITE));
// other policies omitted
});
控制器就像这样,控制器或方法上没有 [Authorize]
属性。
[ApiController]
[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> Get()
{
return Ok(await _service.Get());
}
}
问题是当根据策略属于 ROLE_API_READ
的用户调用 API 方法时,AuthorizationMiddleware 总是将他们标记为未授权。
查看中间件源码,发现:
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
我在我的项目中复制了一份class以便调试,当我在这里设置一个断点时,authorizeData
变成了一个空数组。那么我们检查 EndpointMetadataCollection class:
public IReadOnlyList<T> GetOrderedMetadata<T>() where T : class
{
if (_cache.TryGetValue(typeof(T), out var result))
{
return (T[])result;
}
return GetOrderedMetadataSlow<T>(); // private method that gets the metadata real-time
}
这个_cache
变量在构造函数中设置,包含IAuthorizeData
的空数组:
Metadata
对象本身包含约定添加的 AuthorizeFilter
:
我确实联系了博客作者,令人惊讶的是他似乎不是 Stack Overflow,但我想把它开放给更广泛的社区,看看我的选择是什么。
在此先感谢您的帮助。
过滤器与端点元数据不同。您需要编写一个约定来改变 ActionModel
:
SelectorModel
public class DefaultApiAuthorizationConvention : IActionModelConvention
{
private readonly string _apiReadPolicyName;
private readonly string _apiWritePolicyName;
public DefaultApiAuthorizationConvention(string apiReadPolicyName = Constants.POLICY_API_READ, string apiWritePolicyName = Constants.POLICY_API_WRITE)
{
_apiReadPolicyName = apiReadPolicyName;
_apiWritePolicyName = apiWritePolicyName;
}
public void Apply(ActionModel action)
{
// only apply to API controllers
if (!action.Controller.Attributes.Any(a => a is ApiControllerAttribute))
return;
// Require API Write policy for POST/PUT/DELETE methods, and API Read policy for GET methods
if (action.Attributes.Any(a => a is HttpPostAttribute || a is HttpPutAttribute || a is HttpDeleteAttribute))
{
foreach (var s in action.Selectors)
{
s.EndpointMetadata.Add(new AuthorizeAttribute(_apiWritePolicyName));
}
}
else if (action.Attributes.Any(a => a is HttpGetAttribute))
{
foreach (var s in action.Selectors)
{
s.EndpointMetadata.Add(new AuthorizeAttribute(_apiReadPolicyName));
}
}
}
PS:Auth 在 ASP.NET Core 3.0 中被重写为基于端点元数据,默认情况下不再使用 MVC 过滤器。