将当前用户包含在来自控制器的每个服务层调用中

Ant*_*rim 6 .net c# asp.net asp.net-mvc asp.net-identity

我在我的控制器中执行模型验证,但需要在服务/业务级别进行第二次业务验证.这通常与用户权限有关:当前用户是否可以访问他尝试获取或发布的客户/订单信息?

我的第一个(也是工作的)方法是传递整个User实例或它Id(通过调用User.Identity.GetUserId()),这对于大多数情况来说都是足够的 - 如果不是全部的话.所以我会有这样的事情:

public IHttpActionResult Get(int id)
{
    try
    {
        var customer = customerService.GetById(id, userId);
        return Ok(customer);
    }
    catch (BusinessLogicException e)
    {
        return CreateErrorResponse(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

但我真的不喜欢这样一个事实:通过这种方法,我将不得不在几乎每次调用我的服务层时都包含一个额外的参数.如果我正在调用GetById()方法,我希望通过提供ID来获取内容,而不是ID 用户ID.

一个简单的解决方法是沿着这些方向,这也适用:

public IHttpActionResult Get(int id)
{
    customerService.SetCurrentUser(User.Identity.GetUserId());

    try
    {
        var customer = customerService.GetById(id);
        return Ok(customer);
    }
    catch (BusinessLogicException e)
    {
        return CreateErrorResponse(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我不希望单独调用来设置当前用户,而是希望每次调用服务时都自动执行此操作.我该怎么做?

这是我的服务的样子:

public class CustomerService : EntityService<Customer>, ICustomerService
{
    public string UserId;
    IContext context;
    public CustomerService(IContext context) : base(context)
    {
        this.context = context;
        this.dbSet = context.Set<Customer>();
    }

    public void SetCurrentUser(string userId)
    {
        UserId = userId;
    }

    public DTO.Customer GetById(int id)
    {
        if (!IsAccessibleByUser(id))
        {
            throw new BusinessLogicException(ErrorCode.UserError, "UserId: " + UserId);
        }

        return dbSet.FirstOrDefault(x => x.Id == id).ToDto<Customer, DTO.Customer>();
    }

    public bool IsAccessibleByUser(int id)
    {
        return context.UsersAPI.Any(a => a.AspNetUsersID == UserId);
    }
}
Run Code Online (Sandbox Code Playgroud)

Dar*_*rov 3

我宁愿在自定义授权过滤器中执行此授权逻辑。如果用户未经身份验证或授权,甚至无需访问控制器操作代码。

例如你可以有这样的东西:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }

        var rd = httpContext.Request.RequestContext.RouteData;
        // Get the id of the requested resource from the route data
        string resourceId = rd.Values["id"] as string;
        if (string.IsNullOrEmpty(resourceId))
        {
            // No id of resource was specified => we do not allow access
            return false;
        }

        string userId = httpContext.User.Identity.GetUserId();
        return IsAccessibleByUser(resourceId, userId);
    }

    private bool IsAccessibleByUser(string resourceId, string userId)
    {
        // You know what to do here => fetch the requested resource 
        // from your data store and verify that the current user is
        // authorized to access this resource
    }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以使用自定义属性来装饰需要此类授权的控制器或操作:

[MyAuthorize]
public IHttpActionResult Get(int id)
{
    try
    {
        // At this stage you know that the user is authorized to
        // access the requested resource
        var customer = customerService.GetById(id);
        return Ok(customer);
    }
    catch (BusinessLogicException e)
    {
        return CreateErrorResponse(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,可以通过使用自定义过滤器提供程序来进一步改进此自定义属性,该提供程序允许将数据上下文注入其中,以便您可以执行正确的调用。那么您只能有一个标记属性,过滤器提供程序将使用该属性来决定是否应该执行授权逻辑。