将当前用户包含在来自控制器

时间:2016-03-04 09:32:04

标签: c# asp.net .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);
    }
}

但我并不喜欢这样一个事实,即通过这种方法,我必须在几乎每次调用服务层时都包含一个额外的参数。如果我正在调用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);
    }
}

但是,我不需要单独调用来设置当前用户,而是希望每次调用服务时都自动完成此操作。我该怎么办?

这是我的服务的样子:

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

4 个答案:

答案 0 :(得分:1)

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

例如,你可以这样:

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

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

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

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

答案 1 :(得分:0)

您的服务在执行逻辑时是否需要考虑用户ID?如果是这样,那么将其作为输入的一部分是合理的。 或者它只是更多的授权,服务应拒绝请求而不处理取决于用户是谁(但一旦授权,用户是谁并不重要)? 您可以插入additional data into the header of the WCF request,然后在传入的请求中检查它。但是a)这是一种痛苦,b)客户端应该提供该数据的服务接口并不明显。

因此我在输入中输入了一个用户ID。我不会把它作为服务的财产。它实际上是输入的属性,而不是处理输入的类。

如果您不希望您的服务承担授权请求的额外责任,那么interceptor仍然是一个好主意。这样,您就可以在服务或服务方法上添加属性,并将授权保留在拦截器中。如果用户无法调用服务或方法,则它会在到达服务类之前拒绝该调用。

答案 2 :(得分:0)

迟到的答案,但我理解你的关注,你希望在你的代码中实现的最佳实践并尽可能地保持干​​净,我和你很久以前的问题完全一样,我觉得这不对,应该有更好的方法来做到这一点,在我的情况下,我在我的所有层次上使用:
System.Threading.Thread.CurrentPrincipal.Identity
这个答案在SO上描述了它:https://stackoverflow.com/a/27636802/20126

虽然这是我一直使用的方式,但值得看看别人做了什么:
Accessing HttpContext and User Identity from data layer
How to get User ID inside a separate assembly
Retrieving the current logged in user's Id in a class library outside of an ASP.NET Webforms App using forms authentication
Get the ID of the current user ASP.NET Membership

答案 3 :(得分:0)

您可以直接从以下位置获取用户主体

HttpContext.Current.User

在您的服务中。在检查令牌验证之后或在调用服务之前,需要在授权层中设置用户主体。

但是,我觉得这会使您的服务更受限制于使用某些特定的身份验证过程,除非您未设置用户主体,否则您将无法在其他一些应用程序中使用此服务功能。