Поделиться через


Учебник: Создание и поддержка безопасности веб-API ASP.NET Core с помощью платформы удостоверений Microsoft

Область применения: Зеленый круг с символом белой галочки. арендаторы рабочей силы Зеленый круг с символом белой галочки. внешние арендаторы (узнать больше)

В этом руководстве показано, как защитить веб-API ASP.NET Core с помощью платформы удостоверений Майкрософт, чтобы ограничить доступ только авторизованным пользователям и клиентским приложениям. Веб-API, который вы создаете, использует делегированные разрешения (области) и разрешения приложения (роли приложения).

Изучив это руководство, вы:

  • Создание веб-API ASP.NET Core
  • Настройка веб-API для использования сведений о регистрации приложения Microsoft Entra
  • Защита конечных точек веб-API
  • Запустите веб-API, чтобы убедиться, что он прослушивает HTTP-запросы

Предпосылки

Создание проекта веб-API ASP.NET Core

Чтобы создать минимальный проект веб-API на основе ASP.NET Core, выполните следующие действия.

  1. Откройте терминал в Visual Studio Code или любом другом редакторе кода и перейдите в каталог, в котором вы хотите создать проект.

  2. Выполните следующие команды в интерфейсе командной строки .NET или любом другом средстве командной строки.

    dotnet new web -o TodoListApi
    cd TodoListApi
    
  3. Выберите "Да ", когда диалоговое окно спрашивает, хотите ли вы доверять авторам.

  4. Выберите "Да" , когда диалоговое окно запрашивает, нужно ли добавить необходимые ресурсы в проект.

Установка необходимых пакетов

Чтобы создать, защитить и протестировать веб-API ASP.NET Core, необходимо установить следующие пакеты:

  • Microsoft.EntityFrameworkCore.InMemory— пакет, позволяющий использовать Entity Framework Core с базой данных в памяти. Это полезно для тестирования, но не предназначено для использования в рабочей среде.
  • Microsoft.Identity.Web — набор библиотек ASP.NET Core, упрощающий добавление поддержки проверки подлинности и авторизации в веб-приложения и веб-API, которые интегрируются с платформой удостоверений Майкрософт.

Чтобы установить пакет, используйте следующую команду:

dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web

Настройка сведений о регистрации приложения

Откройте файлappsettings.json в папке приложения и добавьте сведения о регистрации приложения, записанные после регистрации веб-API.

{
    "AzureAd": {
        "Instance": "Enter_the_Authority_URL_Here",
        "TenantId": "Enter_the_Tenant_Id_Here",
        "ClientId": "Enter_the_Application_Id_Here"
    },
    "Logging": {...},
  "AllowedHosts": "*"
}

Замените следующие заполнители, как показано ниже:

  • Замените Enter_the_Application_Id_Here идентификатором приложения (клиента).
  • Замените Enter_the_Tenant_Id_Here идентификатором каталога (клиента).
  • Замените Enter_the_Authority_URL_Here вашим URL-адресом авторитета, как описано в следующем разделе.

URL-адрес авторизации для вашего приложения

URL-адрес центра указывает каталог, из которого библиотека проверки подлинности Microsoft (MSAL) может запрашивать токены. Вы подходите к формированию команды и взаимодействию с арендаторами по-разному, как показано.

//Instance for workforce tenant
Instance: "https://login.microsoftonline.com/"

Использование личного домена URL-адреса (необязательно)

Домены пользовательского URL-адреса не поддерживаются в клиентах рабочей силы.

Добавление разрешений

Все API-интерфейсы должны публиковать как минимум одну область, также называемую делегированным разрешением, чтобы клиентские приложения могли успешно получить токен доступа для пользователя. API также должны публиковать как минимум одну роль приложения, также называемую разрешениями приложения, чтобы клиентские приложения получали токен доступа сами по себе, то есть если они не входят в систему пользователя.

Мы указываем эти разрешения в файлеappsettings.json . В этом руководстве вы зарегистрировали следующие разрешения: делегированные и для приложения.

  • Делегированные разрешения:ToDoList.Read и ToDoList.ReadWrite.
  • Разрешения приложений:ToDoList.Read.All и ToDoList.ReadWrite.All.

Когда пользователь или клиентское приложение вызывает веб-API, доступ к защищенной конечной точке получают только клиенты с этими областями или разрешениями.

{
  "AzureAd": {
    "Instance": "Enter_the_Authority_URL_Here",
    "TenantId": "Enter_the_Tenant_Id_Here",
    "ClientId": "Enter_the_Application_Id_Here",
    "Scopes": {
      "Read": ["ToDoList.Read", "ToDoList.ReadWrite"],
      "Write": ["ToDoList.ReadWrite"]
    },
    "AppPermissions": {
      "Read": ["ToDoList.Read.All", "ToDoList.ReadWrite.All"],
      "Write": ["ToDoList.ReadWrite.All"]
    }
  },
  "Logging": {...},
  "AllowedHosts": "*"
}

Реализация проверки подлинности и авторизации в API

Чтобы настроить проверку подлинности и авторизацию, откройте program.cs файл и замените его содержимое следующими фрагментами кода:

Добавление схемы проверки подлинности

В этом API мы используем схему носителя веб-маркера JSON (JWT) в качестве механизма проверки подлинности по умолчанию. Используйте метод AddAuthentication для регистрации схемы токен-носителя JWT.

// Add required packages to your imports
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Add an authentication scheme
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration);

Создание модели приложения

В корневой папке проекта создайте папку с именем Models. Перейдите в папку Models и создайте файл с именем ToDo.cs , а затем добавьте следующий код.

using System;

namespace ToDoListAPI.Models;

public class ToDo
{
    public int Id { get; set; }
    public Guid Owner { get; set; }
    public string Description { get; set; } = string.Empty;
}

Предыдущий код создает модель с именем ToDo. Эта модель представляет данные, которыми управляет приложение.

Добавление контекста базы данных

Затем мы определим класс контекста базы данных, который координирует функциональные возможности Entity Framework для модели данных. Этот класс наследует от класса Microsoft.EntityFrameworkCore.DbContext , который управляет взаимодействием между приложением и базой данных. Чтобы добавить контекст базы данных, выполните следующие действия.

  1. Создайте папку с именем DbContext в корневой папке проекта.

  2. Перейдите в папку DbContext и создайте файл с именем ToDoContext.cs , а затем добавьте следующий код:

    using Microsoft.EntityFrameworkCore;
    using ToDoListAPI.Models;
    
    namespace ToDoListAPI.Context;
    
    public class ToDoContext : DbContext
    {
        public ToDoContext(DbContextOptions<ToDoContext> options) : base(options)
        {
        }
    
        public DbSet<ToDo> ToDos { get; set; }
    }
    
  3. Откройте файл Program.cs в корневой папке проекта и обновите его следующим кодом:

    // Add the following to your imports
    using ToDoListAPI.Context;
    using Microsoft.EntityFrameworkCore;
    
    //Register ToDoContext as a service in the application
    builder.Services.AddDbContext<ToDoContext>(opt =>
        opt.UseInMemoryDatabase("ToDos"));
    

В приведенном выше фрагменте кода мы регистрируем контекст базы данных в качестве службы с областью действия в поставщике служб приложений ASP.NET Core (также называемом контейнером внедрения зависимостей). Вы также настраиваете ToDoContext класс для использования базы данных в оперативной памяти для API списка ToDo.

Настройка контроллера

Контроллеры обычно реализуют действия создания, чтения, обновления и удаления (CRUD) для управления ресурсами. Так как в этом руководстве основное внимание уделяется защите конечных точек API, мы реализуем только два элемента действия в контроллере. Действие "Чтение всех" для получения всех элементов To-Do и действие "Создать" для добавления нового элемента To-Do. Выполните следующие действия, чтобы добавить контроллер в проект:

  1. Перейдите в корневую папку проекта и создайте папку с именем Контроллеры.

  2. Создайте файл с именем ToDoListController.cs в папке Контроллеры и добавьте следующий шаблонный код:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;
using ToDoListAPI.Models;
using ToDoListAPI.Context;

namespace ToDoListAPI.Controllers;

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ToDoListController : ControllerBase
{
    private readonly ToDoContext _toDoContext;

    public ToDoListController(ToDoContext toDoContext)
    {
        _toDoContext = toDoContext;
    }

    [HttpGet()]
    [RequiredScopeOrAppPermission()]
    public async Task<IActionResult> GetAsync(){...}

    [HttpPost]
    [RequiredScopeOrAppPermission()]
    public async Task<IActionResult> PostAsync([FromBody] ToDo toDo){...}

    private bool RequestCanAccessToDo(Guid userId){...}

    private Guid GetUserId(){...}

    private bool IsAppMakingRequest(){...}
}

Добавление кода в контроллер

В этом разделе объясняется, как добавить код в шаблон контроллера в предыдущем разделе. Основное внимание уделяется защите API, а не его созданию.

  1. Импортируйте необходимые пакеты: Пакет Microsoft.Identity.Web представляет собой оболочку вокруг MSAL.NET, которая помогает легко обрабатывать логику проверки подлинности, например обработку проверки маркеров. Чтобы гарантировать, что для наших конечных точек требуется авторизация, мы используем встроенный Microsoft.AspNetCore.Authorization пакет.

  2. Так как мы предоставили разрешения для вызова этого API с помощью делегированных разрешений от имени пользователя или приложения, где клиент вызывает себя, а не от имени пользователя, важно знать, выполняется ли вызов приложением от собственного имени. Самый простой способ сделать это — определить, содержит ли маркер доступа необязательное утверждение idtyp. Это idtyp утверждение является самым простым способом для API определить, является ли токен токеном приложения или токеном приложения с пользователем. Мы рекомендуем включить необязательное idtyp утверждение.

    Если idtyp утверждение не активировано, можно использовать roles и scp утверждения, чтобы определить, является ли токен доступа токеном приложения или токеном приложения и пользователя. Токен доступа, выданный службой идентификации Microsoft Entra ID, содержит как минимум одно из двух утверждений. Маркеры доступа, выданные пользователю, имеют требование scp. Токены доступа, выданные приложению, имеют утверждение roles. Маркеры доступа, содержащие оба утверждения, выдаются только пользователям, где scp утверждение назначает делегированные разрешения, а roles утверждение обозначает роль пользователя. Маркеры доступа, которые не обладают необходимыми свойствами, не должны учитываться.

    private bool IsAppMakingRequest()
    {
        if (HttpContext.User.Claims.Any(c => c.Type == "idtyp"))
        {
            return HttpContext.User.Claims.Any(c => c.Type == "idtyp" && c.Value == "app");
        }
        else
        {
            return HttpContext.User.Claims.Any(c => c.Type == "roles") && !HttpContext.User.Claims.Any(c => c.Type == "scp");
        }
    }
    
  3. Добавьте вспомогающую функцию, которая определяет, содержит ли запрос достаточно разрешений для выполнения предполагаемого действия. Проверьте, выполняет ли приложение запрос от собственного имени или выполняет ли приложение вызов от имени пользователя, который владеет заданным ресурсом, проверяя идентификатор пользователя.

    private bool RequestCanAccessToDo(Guid userId)
        {
            return IsAppMakingRequest() || (userId == GetUserId());
        }
    
    private Guid GetUserId()
        {
            Guid userId;
            if (!Guid.TryParse(HttpContext.User.GetObjectId(), out userId))
            {
                throw new Exception("User ID is not valid.");
            }
            return userId;
        }
    
  4. Подключите определения разрешений для защиты маршрутов. Защитите API, добавив [Authorize] атрибут в класс контроллера. Это гарантирует, что действия контроллера можно вызывать только в том случае, если API вызывается с авторизованным удостоверением. Определения разрешений определяют, какие типы разрешений необходимы для выполнения этих действий.

    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class ToDoListController: ControllerBase{...}
    

    Добавьте разрешения для конечных точек GET и POST. Для этого используется метод RequiredScopeOrAppPermission , который входит в пространство имен Microsoft.Identity.Web.Resource . Затем вы передаете области и разрешения этому методу с помощью атрибутов RequiredScopesConfigurationKey и RequiredAppPermissionsConfigurationKey .

    [HttpGet]
    [RequiredScopeOrAppPermission(
        RequiredScopesConfigurationKey = "AzureAD:Scopes:Read",
        RequiredAppPermissionsConfigurationKey = "AzureAD:AppPermissions:Read"
    )]
    public async Task<IActionResult> GetAsync()
    {
        var toDos = await _toDoContext.ToDos!
            .Where(td => RequestCanAccessToDo(td.Owner))
            .ToListAsync();
    
        return Ok(toDos);
    }
    
    [HttpPost]
    [RequiredScopeOrAppPermission(
        RequiredScopesConfigurationKey = "AzureAD:Scopes:Write",
        RequiredAppPermissionsConfigurationKey = "AzureAD:AppPermissions:Write"
    )]
    public async Task<IActionResult> PostAsync([FromBody] ToDo toDo)
    {
        // Only let applications with global to-do access set the user ID or to-do's
        var ownerIdOfTodo = IsAppMakingRequest() ? toDo.Owner : GetUserId();
    
        var newToDo = new ToDo()
        {
            Owner = ownerIdOfTodo,
            Description = toDo.Description
        };
    
        await _toDoContext.ToDos!.AddAsync(newToDo);
        await _toDoContext.SaveChangesAsync();
    
        return Created($"/todo/{newToDo!.Id}", newToDo);
    }
    

Настройка ПО промежуточного слоя API для использования контроллера

Затем мы настраиваем приложение для распознавания и использования контроллеров для обработки HTTP-запросов. program.cs Откройте файл и добавьте следующий код, чтобы зарегистрировать службы контроллера в контейнере внедрения зависимостей.


builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();

app.Run();

В приведенном выше фрагменте кода метод подготавливает приложение к использованию контроллеров путем AddControllers() регистрации необходимых служб во время MapControllers() сопоставления маршрутов контроллера для обработки входящих HTTP-запросов.

Запуск API

Запустите API, чтобы убедиться, что он работает без ошибок с помощью команды dotnet run. Если вы планируете использовать протокол HTTPS даже во время тестирования, необходимо доверять сертификату разработки .NET.

  1. Запустите приложение, введя следующее в терминале:

    dotnet run
    
  2. В терминале должны отображаться выходные данные, аналогичные приведенным ниже, что подтверждает, что приложение запущено на http://localhost:{port} и прослушивает запросы.

    Building...
    info: Microsoft.Hosting.Lifetime[0]
        Now listening on: http://localhost:{port}
    info: Microsoft.Hosting.Lifetime[0]
        Application started. Press Ctrl+C to shut down.
    ...
    

Веб-страница http://localhost:{host} отображает выходные данные, аналогичные следующему изображению. Это связано с тем, что API вызывается без проверки подлинности. Чтобы выполнить авторизованный вызов, ознакомьтесь со следующими инструкциями по доступу к защищенному веб-API.

снимок экрана, показывающий ошибку 401 при запуске веб-страницы.

Полный пример этого кода API см. в файле примеров.

Дальнейшие действия