【问题标题】:How to secure endpoints on api resource based on client in identityserver如何根据身份服务器中的客户端保护 api 资源上的端点
【发布时间】:2019-10-31 11:18:00
【问题描述】:

我有自己的授权服务器,构建在 identityserver4 之上,我想保护主机上的所有 api。系统只是对 google 开发人员或 facebook 开发人员的简单模仿,应用程序所有者在其中注册并获取客户端 ID 和客户端密码,以便在 api 上授予访问权限。

所以我在 identityserver4 样本上遵循 client_credentials 流程。一切正常。我为应用程序所有者构建了一个公共 UI,以创建应用程序并选择从他们的应用程序中访问哪些 api。我使用IConfigurationDbContext 进行身份服务器内部表上的CRUD procecces。

问题是我无法根据应用所有者的选择找到保护 api 的方法,当开发人员创建应用并选择一些逻辑端点进行访问时,它们仍然可以访问所有端点。我所做的如下;

授权服务器启动

services.AddIdentityServer()
              .AddDeveloperSigningCredential()
              .AddInMemoryCaching()
              .AddOperationalStore(storeOpitons =>
              {
    storeOpitons.ConfigureDbContext = builder =>
      builder.UseSqlServer(Configuration.GetConnectionString("Default"),
      sql => sql.MigrationsAssembly(migrationsAssembly));
})
              .AddConfigurationStore(storeOptions =>
              {
    storeOptions.ConfigureDbContext = builder =>
      builder.UseSqlServer(Configuration.GetConnectionString("Default"),
      sql => sql.MigrationsAssembly(migrationsAssembly));
});

保存客户端方法

public IActionResult SaveApp(ClientViewModel model, List<SelectedApi> selectedApis)
{
    //ommited for brevity

    Client client = new Client
    {
        Description = model.Description,
        ClientName = model.Name,
        RedirectUris = new[] { model.CallBackUri }
    };
    client.AllowedScopes = selectedApis.Where(a => a.apiValue == "true").Select(a => a.apiName).ToList();
    //e.g : client.AllowedScopes = {"employee_api"};

    _isRepository.SaveClient(client, userApp);

}

Api 项目启动

services.AddAuthentication("Bearer").AddJwtBearer(opt => {
    opt.Authority = "http://localhost:5000";
    opt.Audience = "employee_api";
    opt.RequireHttpsMetadata = false;

});

Api 示例控制器

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : BaseController
{
    private readonly IEmployeeRepository _employeeRepoistory;

    public EmployeeController(IServiceProvider provider, IEmployeeRepository employeeRepository) : base(provider)
    {
        _employeeRepoistory = employeeRepository;

    }
    [HttpGet]
    public IActionResult GetEmployees([FromQuery] EmployeeResourceParameter parameter)
    {
        return Ok(_mapper.Map<IEnumerable<EmployeeModel>>(_employeeRepoistory.GetAll(parameter)));
    }
    [HttpGet("{id:int}")]
    public IActionResult GetEmployeeById(int id)
    {
        var emp = _employeeRepoistory.GetById(id);

        return Ok(_mapper.Map<EmployeeModel>(emp));

    }
}

我想要的是,如果开发人员选择employee_api,他们应该只访问EmployeeController 的端点。但是现在,无论他们的选择是什么,他们都可以访问所有的 api。 在 api 端或身份验证服务器端对此采取哪些步骤?

【问题讨论】:

    标签: c# identityserver4 asp.net-core-3.0


    【解决方案1】:

    终于搞定了。。首先,我意识到掌握ApiResource -&gt; ScopesClients -&gt; AllowedScopes之间的关系很重要。我建议你阅读docshere中关于它们的部分

    当客户端注册到身份服务器然后选择 api 端点(例如:组织、员工、日历)时,它们应该被注册为客户端的 allowedScopes(它们位于 ClientScopes 表中),我以正确的方式做这件事。我做错的是我认为所有这些范围都是 ApiResources( 就我而言,因为我所有的 api 都生活在同一个主机中,我称之为 CommonServiceApi,只有一个 web api 应用程序)。我重新定义了我的 ApiResources 及其范围,如下所示;

    new ApiResource("commonserviceapi", "Common Service API")
    {
        Scopes = {
            new Scope("calender_api", "Calender Api"),
            new Scope("employee_api", "Employee Api"),
            new Scope("organization_api", "Organization Api"),
        }
    }
    

    在 api 方面,应使用 here 指示的策略授权端点。 在访问令牌中,请求客户端的允许范围被传递给 api 应用,因此 api 根据这些值授予访问权限。

    So Api 启动

    services.AddAuthentication("Bearer").AddJwtBearer(opt =>
                {
        opt.Authority = "http://localhost:5000";
        opt.Audience = "commonserviceapi";
    
        opt.RequireHttpsMetadata = false;
    
    });
    
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ApiEmployee", builder =>
        {
            builder.RequireScope("employee_api");
        });
        options.AddPolicy("ApiOrganization", builder =>
        {
            builder.RequireScope("organization_api");
        });
    });
    

    和 Api 控制器

    [Authorize(Policy = "ApiEmployee")]
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : BaseController
    {
        ...
    

    RequireScope顺便说一下是IdentityServer4.AccessTokenValidation包的扩展方法。您应该将此包包含到您的 api 项目中。

    最后,这让我很困惑;从服务器请求访问令牌时,范围参数应该为空,因为 identityserver 从客户端的 allowedScopes 值中获取它。几乎所有样本都填写了该字段,所以您认为应该填写。

    【讨论】:

    • 最后一句话不正确。虽然根据规范,范围参数可能为空,但也允许请求特定范围。它不必为空。不知道为什么你认为它应该。文档中令人困惑的是,它们使用同名的一个资源/范围。确保使用范围名称,而不是资源名称。范围名称用于确定受众(资源名称),而每个请求的范围都添加为声明scope=employee_api。请查看my answer here 了解更多信息。
    • 我希望我能找到你的答案,解释得很好。我并不是说您的范围参数应该始终为空,只是对于这种情况,因为此 allowedscopes 动态来自客户端允许的范围,您必须将此范围参数留空。这是我指出的。
    猜你喜欢
    • 1970-01-01
    • 2017-11-13
    • 2020-05-07
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 2012-04-15
    • 2020-07-20
    • 1970-01-01
    相关资源
    最近更新 更多