3

我有一个习惯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);
        }
    }
}

我的应用程序中有几个其他类似的属性。使用服务定位器可能不是一个好方法。在网上搜索了一下之后,我发现有些人建议改用IAuthorizationFilter依赖注入。但我不知道如何写这种IAuthorizationFilter. 你能帮我写出和上面IAuthorizationFilter一样的东西AuthorizeAttribute吗?

4

2 回答 2

6

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

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

public class GetGroupByIdAttribute : Attribute
{
}

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

[HttpPost]
[GetGroupById]
public IHttpActionResult GetGroupById(int groupId)
{
    //Some code
}

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();
    }
}

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 }
        );            
    }
}

现在,如果需要,我可以创建额外IActionFilters的使用IUserGroupService,然后在应用程序开始时将此服务从WebApiConfig类中注入所有过滤器。

于 2017-05-24T17:50:41.070 回答
0

也许尝试如下所示:

将以下公共方法添加到您的类。

public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
    // gets the dependecies from the serviceProvider 
    // and creates an instance of the filter
    return new GetGroupByIdAuthorizationFilter(
        (IUserGroupService )serviceProvider.GetService(typeof(IUserGroupService )));
}

还将接口添加IFilterMetadata到您的班级。

现在,当要创建您的类时,DI 会注意到有一个 CreateInstance 方法,并将使用该方法而不是构造函数。

或者,您可以通过调用直接从方法中的 DI 获取接口

context.HttpContext.Features.Get<IUserGroupService>()
于 2020-03-04T19:25:56.197 回答