Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Asp.net核心WebAPI基于资源的控制器级别外授权_C#_Asp.net Core Webapi_Asp.net Core 3.1_Jwt Auth - Fatal编程技术网

C# Asp.net核心WebAPI基于资源的控制器级别外授权

C# Asp.net核心WebAPI基于资源的控制器级别外授权,c#,asp.net-core-webapi,asp.net-core-3.1,jwt-auth,C#,Asp.net Core Webapi,Asp.net Core 3.1,Jwt Auth,我正在创建一个Web API,其中用户具有不同的角色,此外,作为任何其他应用程序,我不希望用户a访问用户B的资源。如下图所示: 订单/1(用户A) 订单/2(用户B) 当然,我可以从请求中获取JWT并查询数据库,以检查该用户是否拥有该订单,但这将使我的控制器操作“过于繁重” 这使用了AuthorizeAttribute,但它似乎太宽泛了,我必须为API中的所有路由添加大量的条件,以检查正在访问的路由,然后查询数据库,生成几个连接,这些连接返回到用户表,以返回请求是否有效 更新 对于路线而言,第一

我正在创建一个Web API,其中用户具有不同的角色,此外,作为任何其他应用程序,我不希望用户a访问用户B的资源。如下图所示:

订单/1(用户A

订单/2(用户B

当然,我可以从请求中获取JWT并查询数据库,以检查该用户是否拥有该订单,但这将使我的控制器操作“过于繁重”

这使用了AuthorizeAttribute,但它似乎太宽泛了,我必须为API中的所有路由添加大量的条件,以检查正在访问的路由,然后查询数据库,生成几个连接,这些连接返回到用户表,以返回请求是否有效

更新

对于路线而言,第一道防线是安全策略 需要某些索赔

我的问题是关于负责 确保用户仅访问其数据/资源


在这种情况下,有什么标准方法可以采用吗?

您可以在启动时的ConfigureServices方法中制定多个策略,其中包含角色,或者根据您的示例命名,如下所示:

AddPolicy("UserA", builder => builder.RequireClaim("Name", "UserA"))
或者将“UserA”替换为“Accounting”,将“Name”替换为“Role”。
然后按角色限制控制器方法:

[Authorize(Policy = "UserA")

当然,这又是在控制器级别进行的,但您不必绕过令牌或数据库。这将直接指示您的角色或用户可以使用什么方法。

您的陈述是错误的,您的设计也是错误的

这个链接可以总结为“在声明性能不起作用之前测试性能”

使用标识(jwt令牌或您配置的任何东西)来检查实际用户是否正在访问正确的资源(或者最好只为其拥有的资源提供服务)不是太重。 如果它变得沉重,说明你做错了什么。 这可能是因为你有大量的同时访问,你只需要缓存一些数据,比如字典顺序->所有者ID,随着时间的推移会被清除。。。但情况似乎并非如此

关于设计:制作一个可重用的服务,该服务可以被注入,并且有一个方法来访问您需要的每一个接受用户的资源(IdentityUser,或jwtsubject,只是用户id,或任何您拥有的)

差不多

ICustomerStore 
{
    Task<Order[]> GetUserOrders(String userid);
    Task<Bool> CanUserSeeOrder(String userid, String orderid);
}
icCustomerStore
{
任务GetUserOrders(字符串userid);
任务CanUserSeeOrder(字符串用户ID、字符串医嘱ID);
}

相应地实现并使用此类系统地检查用户是否可以访问资源。

使用
[Authorize]
属性称为声明性授权。但它是在控制器或操作执行之前执行的。如果需要基于资源的授权,并且文档具有author属性,则必须在授权评估之前从存储中加载文档。这叫做强制授权

关于Microsoft文档如何在ASP.NET Core中处理强制授权,有一个详细的介绍。我认为它相当全面,它回答了您关于标准方法的问题


您还可以找到代码示例

我采取的方法是自动将查询限制为当前已验证用户帐户拥有的记录

我使用一个接口来指示哪些数据记录是特定于帐户的

public interface IAccountOwnedEntity
{
    Guid AccountKey { get; set; }
}
并提供一个接口来注入逻辑,以确定存储库应该针对哪个帐户

public interface IAccountResolver
{
    Guid ResolveAccount();
}

我今天使用的IAccountResolver的实现是基于经过身份验证的用户声明的

public class ClaimsPrincipalAccountResolver : IAccountResolver
{
    private readonly HttpContext _httpContext;

    public ClaimsPrincipalAccountResolver(IHttpContextAccessor httpContextAccessor)
    {
        _httpContext = httpContextAccessor.HttpContext;
    }

    public Guid ResolveAccount()
    {
        var AccountKeyClaim = _httpContext
            ?.User
            ?.Claims
            ?.FirstOrDefault(c => String.Equals(c.Type, ClaimNames.AccountKey, StringComparison.InvariantCulture));

        var validAccoutnKey = Guid.TryParse(AccountKeyClaim?.Value, out var accountKey));

        return (validAccoutnKey) ? accountKey : throw new AccountResolutionException();
    }
}
然后在存储库中,我将所有返回的记录限制为该帐户所有

public class SqlRepository<TRecord, TKey>
    where TRecord : class, IAccountOwnedEntity
{
    private readonly DbContext _dbContext;
    private readonly IAccountResolver _accountResolver;

    public SqlRepository(DbContext dbContext, IAccountResolver accountResolver)
    {
        _dbContext = dbContext;
        _accountResolver = accountResolver;
    }

    public async Task<IEnumerable<TRecord>> GetAsync()
    {
        var accountKey = _accountResolver.ResolveAccount();

        return await _dbContext
                .Set<TRecord>()
                .Where(record => record.AccountKey == accountKey)
                .ToListAsync();
    }


    // Other CRUD operations
}
公共类SqlRepository
where TRecord:class,IAccountOwnedEntity
{
私有只读dbcontextu DbContext;
私有只读IAccountResolver\u accountResolver;
公共SqlRepository(DbContext DbContext,IAccountResolver accountResolver)
{
_dbContext=dbContext;
_accountResolver=accountResolver;
}
公共异步任务GetAsync()
{
var accountKey=_accountResolver.resolveCount();
返回等待_dbContext
.Set()
.Where(record=>record.AccountKey==AccountKey)
.ToListAsync();
}
//其他积垢作业
}
使用这种方法,我不必记得对每个查询应用我的帐户限制。它只是自动发生的

以确保用户A无法查看Id为2(属于用户B)的订单。我会做以下两件事之一:

One: 有一个
GetOrderByIdAndUser(长orderId,字符串用户名)
,当然您可以从jwt获取用户名。 如果用户没有自己的订单,他将看不到它,并且没有额外的数据库调用

两个: 首先从数据库中获取订单
GetOrderById(long orderId)
,然后验证订单的username属性是否与jwt中登录的用户相同。 如果用户不拥有order Throw异常,则返回404或其他任何内容,并且没有额外的db调用

void ValidateUserOwnsOrder(Order order, string username)
{
            if (order.username != username)
            {
                throw new Exception("Wrong user.");
            }
}

您需要回答的第一个问题是“我什么时候可以做出授权决定?”。你什么时候真正有进行检查所需的信息

如果您几乎总是可以确定从路由数据(或其他请求上下文)访问的资源,则可能适合使用。当您与由资源明确存储的数据交互时,这种方法最有效——因为它对列表过滤等工作毫无帮助,在这些情况下,您必须退回到强制检查

如果在实际检查资源之前,您无法确定用户是否可以摆弄资源,那么您将不得不进行强制检查。这是有道理的,但它没有政策框架那么有用。在某个时候,编写一个
IUserContext
whi可能很有价值