问题
I would like to display components of a navigation tree only if a principal has access to those components (whether roles or policy based).
I am not particularly familiar with MVC core, but reading to date would indicate that I could build a list of what authorization filters will be applied to an action with something like:
public NavigationNodePermissionResolver(ApplicationModel appModel)
{
foreach (var controllerModel in appModel.Controllers)
{
var contextFilters = controllerModel.Filters.OfType<IAsyncAuthorizationFilter>().ToList();
foreach (ActionModel action in controllerModel.Actions)//todo restrain to HttpGet
{
var actionFilters = action.Filters.OfType<IAsyncAuthorizationFilter>().ToList();
}
}
How can I inject an instance of the application model into the instantiator for NavigationNodePermissionResolver?
NavigationNodePermissionResolver itself will be injectable as:
services.TryAddSingleton<INavigationNodePermissionResolver, NavigationNodePermissionResolver>();
edit
the follow on to this question - what to do when I have a list of the IAsyncAuthorizationFilters is in this so question.
as follow up information the ApplicationModel is a property of the ApplicationModelProviderContext which I found out about in this SO answer - it does seem to be roughly what I am after, but it may be the approach I am using to obtain and then execute authorization filters is completely wrong
回答1:
I discovered I can instantiate and inject the same instance which implements 2 interfaces. I ended up within Startup.ConfigureServices:
var customAppModelProvider = new CustomApplicationModelProvider();
services.AddSingleton<IApplicationModelProvider>(customAppModelProvider);
services.AddSingleton<IActionFilterMap>(customAppModelProvider);
And the implementation:
public interface IActionFilterMap
{
IEnumerable<IAsyncAuthorizationFilter> GetFilters(string area, string controller, string action);
}
public class CustomApplicationModelProvider : IApplicationModelProvider, IActionFilterMap
{
//It will be executed after AuthorizationApplicationModelProvider, which has order -990
public int Order => 0;
private ReadOnlyDictionary<ActionKey, IEnumerable<IAsyncAuthorizationFilter>> _authDictionary;
public IEnumerable<IAsyncAuthorizationFilter> GetFilters(string area, string controller, string action)
{
var key = new ActionKey(area, controller, action);
if (_authDictionary.TryGetValue(key, out IEnumerable<IAsyncAuthorizationFilter> returnVar))
{
return returnVar;
}
return null;//returning null rather than Enumerable.Empty so consuming method can detect if action found and has no Authorization, or action not found
}
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
var returnVar = new Dictionary<ActionKey, IEnumerable<IAsyncAuthorizationFilter>>();
foreach (var controllerModel in context.Result.Controllers)
{
var controllerFilters = controllerModel.Filters.OfType<IAsyncAuthorizationFilter>().ToList();
string area = controllerModel.Attributes.OfType<AreaAttribute>().FirstOrDefault()?.RouteValue;
foreach (ActionModel action in controllerModel.Actions)//todo restrain to get
{
var method = action.Attributes.OfType<HttpMethodAttribute>().FirstOrDefault();
if (method == null || method.HttpMethods.Contains("GET"))
{
var key = new ActionKey(area, controllerModel.ControllerName, action.ActionName);
if (action.Filters.OfType<AllowAnonymousFilter>().Any())
{
returnVar.Add(key, Enumerable.Empty<IAsyncAuthorizationFilter>());
}
else
{
var filters = controllerFilters.Concat(action.Filters.OfType<IAsyncAuthorizationFilter>()).ToArray();
returnVar.Add(key, filters);
}
}
}
_authDictionary = new ReadOnlyDictionary<ActionKey, IEnumerable<IAsyncAuthorizationFilter>>(returnVar);
}
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
//empty
}
private class ActionKey : Tuple<string, string, string>
{
public ActionKey(string area, string controller, string action) : base(area ?? string.Empty, controller, action)
{
_hashCode = base.GetHashCode();
}
int _hashCode;
public override int GetHashCode()
{
return _hashCode;
}
}
}
来源:https://stackoverflow.com/questions/45023428/injectiong-an-instance-of-applicationmodel-into-an-mvc-service-which-will-itself