如何重写代码以将IAuthorizationFilter与依赖项注入一起使用,而不是将Asize Net Web Api中具有服务位置的AuthorizeAttribute与使用?

Myk*_*ych 2 asp.net authorize-attribute asp.net-web-api iauthorizationfilter asp.net-web-api-filters

我有一个习惯AuthorizeAttribute,在授予用户查看资源的权限之前,需要使用其中一个业务层服务来验证数据库中的某些数据。为了能够在我内部分配此服务,我AuthorizeAttribute决定使用服务位置“反模式”,这是代码:

internal class AuthorizeGetGroupByIdAttribute : AuthorizeAttribute
{
    private readonly IUserGroupService _userGroupService;

    public AuthorizeGetGroupByIdAttribute()
    {
        _userGroupService = ServiceLocator.Instance.Resolve<IUserGroupService>();
    }

    //In this method I'm validating whether the user is a member of a group. 
    //If they are not they won't get a permission to view the resource, which is decorated with this attribute.
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        Dictionary<string, string> parameters = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
        int groupId = int.Parse(parameters["groupId"]);
        int currentUserId = HttpContext.Current.User.Identity.GetUserId();

        return _userGroupService.IsUserInGroup(currentUserId, groupId);
    }

    protected override void HandleUnauthorizedRequest(HttpActionContext actionContex)
    {
        if (!HttpContext.Current.User.Identity.IsAuthenticated)
        {
            base.HandleUnauthorizedRequest(actionContex);
        }
        else
        {
            actionContex.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,我还有其他几个类似的属性。使用服务定位器可能不是一个好方法。在网上搜索了一点之后,我发现有些人建议改为使用IAuthorizationFilter依赖项注入。但是我不知道该如何写IAuthorizationFilter。您能帮我写出IAuthorizationFilterAuthorizeAttribute上述内容相同的内容吗?

Myk*_*ych 5

因此,经过一段时间的努力,我认为我设法解决了这个问题。为此,您必须执行以下步骤:

1)首先,您必须设置GetGroupByIdAttribute被动属性,被动是指一个空属性,其中没有任何逻辑(它将严格用于装饰目的)

public class GetGroupByIdAttribute : Attribute
{
}
Run Code Online (Sandbox Code Playgroud)

2)然后,您必须使用此属性标记要为其添加授权的控制器方法。

[HttpPost]
[GetGroupById]
public IHttpActionResult GetGroupById(int groupId)
{
    //Some code
}
Run Code Online (Sandbox Code Playgroud)

3)为了自己编写IAuthorizationFilter,必须实现其方法ExecuteAuthorizationFilterAsync。这是完整的课程(我提供了一些注释来指导您完成代码):

public class GetGroupByIdAuthorizationFilter : IAuthorizationFilter
{
    public bool AllowMultiple { get; set; }

    private readonly IUserGroupService _userGroupService;

    //As you can see I'm using a constructor injection here
    public GetGroupByIdAuthorizationFilter(IUserGroupService userGroupService)
    {
        _userGroupService = userGroupService;
    }

    public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        //First I check whether the method is marked with the attribute, if it is then check whether the current user has a permission to use this method
        if (actionContext.ActionDescriptor.GetCustomAttributes<GetGroupByIdAttribute>().SingleOrDefault() != null)
        {
            Dictionary<string, string> parameters = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
            int groupId = int.Parse(parameters["groupId"]);
            int currentUserId = HttpContext.Current.User.Identity.GetUserId();

            //If the user is not allowed to view view the resource, then return 403 status code forbidden
            if (!_userGroupService.IsUserInGroup(currentUserId, groupId))
            {
                return Task.FromResult(new HttpResponseMessage(HttpStatusCode.Forbidden));
            }
        }
        //If this line was reached it means the user is allowed to use this method, so just return continuation() which basically means continue processing 
        return continuation();
    }
}
Run Code Online (Sandbox Code Playgroud)

4)最后一步是在中注册过滤器WebApiConfig

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Here I am registering Dependency Resolver
        config.DependencyResolver = ServiceLocator.Instance.DependencyResolver;

        //Then I resolve the service I want to use (which should be fine because this is basically the start of the application)
        var userGroupService = ServiceLocator.Instance.Resolve<IUserGroupService>();

        //And finally I'm registering the IAuthorizationFilter I created 
        config.Filters.Add(new GetGroupByIdAuthorizationFilter(userGroupService));

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );            
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果需要,我可以创建其他IActionFilters用途IUserGroupService,然后在应用程序的开头(从WebApiConfig类)向所有过滤器中注入此服务。