Как защитить конечные точки на ресурсе api на основе клиента на сервере идентификации

У меня есть собственный сервер авторизации, созданный на основе identityserver4, где я хочу защитить все API на хосте. Система - это просто имитация разработчиков Google или Facebook, где владельцы приложений регистрируются и получают идентификатор клиента и секреты клиента для предоставления доступа на API.

Итак, я проследил за потоком client_credentials в образцах identityserver4. Все работает нормально. Я создал общедоступный пользовательский интерфейс для владельцев приложений, чтобы они могли создавать приложения и выбирать API, к которым они будут обращаться из своих приложений. Я использую IConfigurationDbContext для процедур CRUD на внутренних таблицах сервера идентификации.

Проблема в том, что я не мог найти способ защитить apis на основе выбора владельцев приложений, когда разработчик создает приложение и выбирает несколько логических конечных точек для доступа, они по-прежнему могут достичь всех конечных точек. Я сделал следующее:

Запуск сервера авторизации

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 или на стороне сервера аутентификации?

введите описание изображения здесь


person ibubi    schedule 31.10.2019    source источник


Ответы (1)


Наконец-то я это сделал ... Прежде всего, я понял, что важно понять связь между ApiResource -> Scopes, Clients -> AllowedScopes. Я предлагаю вам прочитать части о них в документации и здесь

Когда клиент регистрируется на сервере идентификации и затем выбирает конечные точки API (например: организация, сотрудник, календарь), он должен быть зарегистрирован как allowedScopes клиента (они живут в таблице ClientScopes ), Я делал это правильно. Что я делал неправильно, так это полагаю, что все эти области являются ApiResources (в моем случае, потому что все мои API-интерфейсы находятся на одном хосте, который я называю CommonServiceApi, всего одно приложение веб-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 конечные точки должны быть авторизованы с помощью политик, как указано здесь. В токене доступа приложению api передаются разрешенные области запрашивающего клиента, поэтому api предоставляет доступ в соответствии с этими значениями.

Итак, 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.

И, наконец, это сбивало меня с толку; при запросе токена доступа с сервера параметр области должен быть пустым, поскольку сервер идентификации берет его из значений allowdScopes клиента. Почти все образцы заполняли это поле, так что можно подумать, что его нужно заполнить.

person ibubi    schedule 31.10.2019
comment
Последнее замечание неверно. Хотя по спецификации параметр области может быть пустым, ему также разрешено запрашивать определенные области. Он не обязательно должен быть пустым. Не уверен, почему вы так думаете. Что сбивает с толку в документации, так это то, что они используют один ресурс / область с тем же именем. Убедитесь, что используется имя области, а не имя ресурса. Имя области используется для определения аудитории (имя ресурса), в то время как каждая запрошенная область добавляется как утверждение scope=employee_api. Взгляните на мой ответ здесь для получения дополнительной информации. - person Ruard van Elburg; 01.11.2019
comment
Хотел бы я найти ваш ответ раньше, очень хорошо объясненный. Я не имел в виду, что ваш параметр области всегда должен быть пустым, просто для этого случая, поскольку это разрешенные области динамически поступают из разрешенных областей действия клиента, вы должны оставить этот параметр области пустым. Это то, что я указал. - person ibubi; 01.11.2019