Posted on 5. November 2023

What’s new with identity in .NET 8

Що нового в ідентифікації в .NET 8


У квітні 2023 року я писав про зобов’язання команди ASP.NET Core покращити автентифікацію, авторизацію та керування ідентифікацією в .NET 8. Представлений нами план включав три основні результати:


– Нові API для спрощення входу та керування ідентифікацією для клієнтських програм, таких як Single Page Apps (SPA) і Blazor WebAssembly.

– Увімкнення автентифікації та авторизації на основі маркерів у ASP.NET Core Identity для клієнтів, які не можуть використовувати файли cookie.

– Покращення в документації.


Усі три результати з’являтья разом із .NET 8. Крім того, ми змогли додати новий інтерфейс ідентифікації для веб-програм Blazor, який працює з обома новими режимами візуалізації, сервером і WebAssembly.


Давайте розглянемо кілька сценаріїв, уможливлених новими змінами в .NET 8. У цій публікації блогу ми розглянемо:

– Захист простого серверного веб-API

– Використання нового інтерфейсу ідентифікатора Blazor

– Додавання зовнішнього логіна, наприклад Google або Facebook

– Захист програм Blazor WebAssembly за допомогою вбудованих функцій і компонентів

– Використання токенів для клієнтів, які не можуть використовувати файли cookie


Давайте розглянемо найпростіший сценарій використання нових функцій ідентифікації.

Базовий сервер веб-API

Простий спосіб використання нової авторизації — увімкнути її в базовій програмі Web API. Цю ж програму також можна використовувати як серверну частину для Blazor WebAssembly, Angular, React та інших односторінкових веб-програм (SPA). Якщо ви починаєте з проекту ASP.NET Core Web API у .NET 8, який включає OpenAPI, ви можете додати автентифікацію за кілька кроків.


Ідентифікація є “опціональною”, тож залишилося додати ще кілька пакунків:

Microsoft.AspNetCore.Identity.EntityFrameworkCoreпакет, який забезпечує інтеграцію EF Core

– Пакет для бази даних, яку ви бажаєте використати, наприклад Microsoft.EntityFrameworkCore.SqlServer(у цьому прикладі ми використаємо базу даних у пам’яті)

 

Ви можете додати ці пакети за допомогою менеджера пакетів NuGet або командного рядка. Наприклад, щоб додати пакети за допомогою командного рядка, перейдіть до теки проєкту та виконайте такі команди dotnet:

 

Ідентифікація дозволяє налаштувати як інформацію про користувача, так і базу даних користувача, якщо у вас є вимоги, що виходять за межі того, що надається в рамках .NET Core. Для нашого базового прикладу ми просто використаємо інформацію про користувача та базу даних за замовчуванням. Для цього ми додамо новий клас до проєкту під назвою, MyUser, який успадковує від IdentityUser:

 

Додайте новий клас під назвою AppDbContext, який успадковує від IdentityDbContext:

Надання спеціального конструктора дає змогу налаштувати базу даних для різних середовищ.


Щоб налаштувати ідентифікатор для програми, відкрийте файл Program.cs. Налаштуйте ідентифікатор на використання автентифікації на основі файлів cookie та ввімкніть перевірку авторизації, додавши наступний код після виклику WebApplication.CreateBuilder(args):

 

Налаштуйте базу даних EF Core. Тут ми використаємо базу даних у пам’яті та назвемо її «AppDb». Вона використовується тут для демонстрації, тому можна легко перезапустити програму та перевірити потік, щоб зареєструватися та ввійти (кожний запуск розпочинатиметься з новою базою даних). Перехід на SQLite збереже користувачів між сеансами, але вимагає належного створення бази даних за допомогою міграцій, як показано в цьому посібнику з початку роботи з EF Core. Ви можете використовувати інші реляційні постачальники, такі як SQL Server, для свого робочого коду.

 

Налаштуйте ідентифікацію для використання бази даних EF Core та відкрийте кінцеві точки ідентифікації:

 

Позначте маршрути для кінцевих точок ідентифікації. Цей код слід розмістити після виклику builder.Build():

Тепер програма готова до автентифікації та авторизації! Щоб захистити ендпоінт, використовуйте метод розширення .RequireAuthorization(), де ви визначаєте маршрут авторизації. Якщо ви використовуєте рішення на основі контролера, ви можете додати атрибут [Authorize]до контролера або дії.

Щоб протестувати програму, запустіть її та перейдіть до інтерфейсу користувача Swagger. Розгорніть захищений ендпоінт, оберіть «випробувати» та виберіть «Виконати». Ендпоінт отримує повідомлення 404 – not found, що, мабуть, є більш безпечним, ніж повідомлення, 401 – not authorized оскільки воно не показує, що кінцева точка існує.

Swagger UI з 404

Тепер розгорніть /register і заповніть свої облікові дані. Якщо ви введете недійсну адресу електронної пошти або неправильний пароль, результат міститиме помилки перевірки.

Swagger UI з помилками перевірки

Помилки в цьому прикладі повертаються у форматі ProblemDetails , щоб ваш клієнт міг легко проаналізувати їх і за потреби відобразити помилки перевірки. Я покажу приклад цього в автономній програмі Blazor WebAssembly.


Успішна реєстрація призводить до відповіді 200 – OK. Тепер ви можете розгорнути /login та ввести ті самі облікові дані. Зауважте, що для цього прикладу є додаткові параметри, які можна видалити. Обов’язково встановіть useCookies true. Успішний вхід призводить до відповіді 200 – OK з файлом cookie в заголовку відповіді.

Тепер ви можете повторно запустити захищений ендпоінт, і він має повернути дійсний результат. Це тому, що автентифікація на основі файлів cookie надійно вбудована у ваш браузер і «просто працює». Ви щойно захистили свою першу кінцеву точку за допомогою ідентифікації!


Деякі веб-клієнти можуть не включати файли cookie в заголовок за умовчанням. Якщо ви використовуєте інструмент для тестування API, вам може знадобитися ввімкнути файли cookie в налаштуваннях. JavaScript API fetch не включає файли cookie за замовчуванням. Ви можете ввімкнути їх, встановивши значення credentials як  include в параметрах. Подібним чином, HttpClient запущений в програмі Blazor WebAssembly потребує HttpRequestMessage для включення облікових даних, наприклад:

Далі перейдемо до веб-програми Blazor.


Інтерфейс ідентифікації Blazor


Велика мета нашої команди, якої ми змогли досягти, полягала в тому, щоб реалізувати користувальницький інтерфейс ідентифікації, який включає параметри реєстрації, входу та налаштування багатофакторної автентифікації в Blazor. Інтерфейс користувача вбудовано в шаблон, коли ви вибираєте параметр «Індивідуальні облікові записи» для автентифікації. На відміну від попередньої версії інтерфейсу ідентифікації, яка була прихована, якщо ви не хотіли її налаштувати, шаблон генерує весь вихідний код, щоб ви могли змінювати його за потреби. Нова версія створена на основі компонентів Razor і працює як із серверними програмами, так і з програмами WebAssembly Blazor.

Сторінка входу Blazor

Нова веб-модель Blazor дозволяє вам налаштувати, чи інтерфейс користувача відображатиметься на стороні сервера чи клієнта, що працює в WebAssembly. Якщо ви обираєте режим WebAssembly, сервер усе одно оброблятиме всі запити на автентифікацію та авторизацію. Він також створить код для спеціальної реалізації AuthenticationStateProvider, яка відстежує стан автентифікації. Постачальник використовує  клас PersistentComponentState для попереднього відтворення стану автентифікації та збереження його на сторінці. PersistentAuthenticationStateProvider у клієнтській програмі WebAssembly використовує компонент для синхронізації стану автентифікації між сервером і браузером. Постачальник стану також може бути названий PersistingRevalidatingAuthenticationStateProvider під час роботи з автоматичною інтерактивністю або IdentityRevalidatingAuthenticationStateProvider для інтерактивності сервера.

Незважаючи на те, що приклади в цій публікації блогу зосереджені на простому сценарії входу з іменем користувача та паролем, ASP.NET Identity підтримує взаємодію на основі електронної пошти, як-от підтвердження облікового запису та відновлення пароля. Також можна налаштувати багатофакторну автентифікацію. Компоненти для всіх цих функцій включені в інтерфейс користувача.

Додайте зовнішній логін

Нам часто задають питання, як інтегрувати зовнішні входи через соціальні веб-сайти з ASP.NET Core Identity. Починаючи з проєкту веб-програми Blazor за замовчуванням, ви можете додати зовнішній логін за кілька кроків.


По-перше, вам потрібно буде зареєструвати свою програму на веб-сайті соціальної мережі. Наприклад, щоб додати логін Twitter, перейдіть на портал розробників Twitter і створіть нову програму. Вам потрібно буде надати певну базову інформацію, щоб отримати облікові дані клієнта. Після створення програми перейдіть до налаштувань програми та натисніть «редагувати» під час автентифікації. Укажіть «нативний додаток» для типу додатка, щоб потік працював правильно, і ввімкніть «запитувати електронні листи від користувачів». Вам потрібно буде надати URL-адресу зворотного виклику. У цьому прикладі ми використаємо URL-адресу зворотного виклику https://localhost:5001/signin-twitter за умовчанням для шаблону веб-програми Blazor. Ви можете просто замінити цю адресу, щоб відповідати URL-адресі вашої програми (тобто замінити 5001 своїм власним портом). Також зверніть увагу на ключ і секрет API.

Далі додайте до своєї програми відповідний пакет автентифікації. Існує список постачальників соціальної автентифікації OAuth 2.0 для ASP.NET Core, який підтримується спільнотою, із багатьма варіантами на вибір. Ви можете комбінувати кілька зовнішніх входів за потреби. Для Twitter я додам пакет AspNet.Security.OAuth.Twitter.

 

З командного рядка в кореневому каталозі проєкту сервера виконайте цю команду, щоб зберегти ключ API (ідентифікатор клієнта) і секрет.

 

Нарешті, налаштуйте вхід в Program.cs, замінивши цей код:

з цим кодом:

Файли cookie є кращим і найбезпечнішим підходом для впровадження ASP.NET Core Identity. Токени підтримуються за потреби та потребують налаштування IdentityConstants.BearerScheme. Токени є пропрієтарними, а потік на основі токенів призначений для простих сценаріїв, тому він не реалізує стандарти OAuth 2.0 або OIDC.

Що далі? Вірите чи ні, ви закінчили. Цього разу, коли ви запускаєте програму, сторінка входу автоматично визначить зовнішній логін і надасть кнопку для його використання.

Сторінка входу Blazor у Twitter

Коли ви ввійдете в систему та авторизуєтесь у програмі, вас буде перенаправлено назад і відбудеться автентифікація.

Захист програм Blazor WebAssembly

Основною мотивацією для додавання нових ідентифікаційних API було  бажання полегшити розробникам захист їхніх браузерних програм, включаючи Single Page Apps (SPA) і Blazor WebAssembly. Немає значення, чи використовуєте ви вбудований постачальник ідентифікації, спеціальну систему входу чи хмарну службу, як-от Microsoft Entra, кінцевим результатом буде ідентифікація, яка або автентифікована за допомогою претензій і ролей, або не автентифікована. У Blazor ви можете захистити компонент Razor, додавши атрибут [Authorize] до компонента або до сторінки, на якій розміщено компонент. Ви також можете захистити маршрут, додавши .RequireAuthorization()метод розширення до визначення маршруту.

Повний вихідний код для цього прикладу доступний у сховищі зразків Blazor.

Тег AuthorizeView забезпечує простий спосіб обробки вмісту, до якого користувач має доступ. Доступ до стану автентифікації можна отримати через властивість context. Зверніть увагу на наступне:

Привітання буде показано всім. У випадку Blazor WebAssembly, коли клієнту може знадобитися асинхронна автентифікація через виклики API, вміст Authorizing буде показано під час запиту та вирішення стану автентифікації. Потім, залежно від того, пройшли ви автентифікацію чи ні, ви побачите своє ім’я або повідомлення про те, що ви не автентифіковані. Як саме клієнт дізнається, чи ви автентифіковані? Ось де з’являється AuthenticationStateProvider.

Сторінка App.razor загорнута в провайдера CascadingAuthenticationState. Цей провайдер відповідає за відстеження стану автентифікації та надання до нього доступу для решти програми. AuthenticationStateProvider впроваджується в провайдера та використовується для відстеження стану. AuthenticationStateProvider також вводиться в компонент AuthorizeView. Коли стан автентифікації змінюється, постачальник сповіщає компонент AuthorizeView, і вміст оновлюється відповідно.

 

По-перше, ми хочемо переконатися, що виклики API відповідно зберігають облікові дані. Для цього я створив обробник під назвою CookieHandler.

 

В Program.cs додав обробник до HttpClient і використав фабрику клієнтів, щоб налаштувати спеціальний клієнт для автентифікації.

 

AuthUrl - це URL-адреса сервера ASP.NET Core, на якому доступні API ідентифікації. Далі я створив CookieAuthenticationStateProvider, який успадковує AuthenticationStateProvider і перевизначає метод GetAuthenticationStateAsync. Основна логіка виглядає так:

Ендпоінт інформації про користувача захищений, тому, якщо користувач не автентифікований, запит не вдасться виконати, і метод поверне неавтентифікований стан. В іншому випадку він створює відповідну ідентифікацію та заявляє та повертає автентифікований стан.


Як програма дізнається, коли стан змінився? Ось як виглядає вхід із Blazor WebAssembly за допомогою ідентифікаційного API:

Після успішного входу викликається метод NotifyAuthenticationStateChanged базового класу AuthenticationStateProvider, щоб повідомити провайдера про зміну стану. Йому передається результат запиту нового стану автентифікації, щоб він міг перевірити наявність файлу cookie. Після цього провайдер оновить компонент AuthorizeView, і користувач побачить автентифікований вміст.

Токени

У рідкісних випадках, коли ваш клієнт не підтримує файли cookie, API входу надає параметр для запиту токенів. Видається спеціальний токен (який є власністю платформи ідентифікації ASP.NET Core), який можна використовувати для автентифікації наступних запитів. Токен передається в хедері  Authorization як маркер токена. Також надається токен оновлення. Це дозволяє вашій програмі запитувати новий токен, коли закінчується термін дії старого, не змушуючи користувача знову входити в систему. Токени не є стандартними веб-токенами JSON (JWT). Це рішення було прийнято навмисно, оскільки вбудована ідентифікація призначена в основному для простих сценаріїв. Параметр маркера не призначений для повнофункціонального постачальника послуг ідентифікації або сервера маркерів, а замість цього є альтернативою параметру cookie для клієнтів, які не можуть використовувати файли cookie.

Не впевнені, чи потрібен вам сервер токенів чи ні? Прочитайте документ, який допоможе вам вибрати правильне рішення ідентифікації ASP.NET Core. Шукаєте більш просунуте рішення ідентифікації? Прочитайте наш список рішень для керування ідентифікацією для ASP.NET Core.

Документи та зразки

Третій результат – документація та зразки. Ми вже представили нову документацію та будемо додавати нові статті та зразки, коли наблизимось до релізу .NET 8. Слідкуйте за релізом № 29452 – документація та зразки для ідентифікації в .NET 8, щоб відстежувати прогрес. Будь ласка, використовуйте випуск, щоб надіслати додаткову документацію або зразки, які ви шукаєте. Ви також можете посилатися на конкретні проблеми для різних документів і залишати там свої відгуки.

Висновок

Нові функції ідентифікації в .NET 8 роблять захист ваших програм простішим, ніж будь-коли. Якщо ваші вимоги прості, тепер ви можете додати автентифікацію та авторизацію до своєї програми за допомогою кількох рядків коду. Нові API дають змогу захистити кінцеві точки веб-API за допомогою автентифікації та авторизації на основі файлів cookie. Існує також опція на основі маркерів для клієнтів, які не можуть використовувати файли cookie.


Дізнайтеся більше про нові функції ідентифікації в документації ASP.NET Core .


Source




Posted on 13. December 2022

.NET 7 Покращення мережевих можливостей


.NET 7 Покращення мережевих можливостей

Оскільки нещодавно вийшов .NET 7, хотілося б познайомити вас з деякими цікавими змінами та доповненнями, зробленими в мережевому просторі. У цій статті йтиметься про зміни .NET 7 у просторі HTTP, нові API-інтерфейси QUIC, мережеву безпеку та WebSockets.

HTTP

Удосконалена обробка невдалих спроб з’єднання

У версіях до .NET 6, якщо в пулі з’єднань не було доступного з’єднання, новий HTTP-запит завжди створював нову спробу з’єднання і чекав на неї (якщо це дозволяли налаштування обробника, наприклад, MaxConnectionsPerServer для HTTP/1.1 або EnableMultipleHttp2Connections для HTTP/2). Недоліком цього сценарію є те, що якщо встановлення цього з’єднання займе деякий час, а тим часом стане доступним інше з’єднання, цей запит продовжить очікувати на з’єднання, яке він створив, що призведе до збільшення затримки. У .NET 6.0 було замінено це на обробку запитів на тому з’єднанні, яке стане доступним першим, незалежно від того, чи це щойно створене з’єднання, чи те, яке стало готовим до обробки запиту в цей час. Нове з’єднання все одно створюється (з урахуванням обмежень), і якщо воно не було використане ініціатором запиту, воно об’єднується в пул, щоб наступні запити могли його використати.

На жаль, реалізація .NET 6.0 виявилася проблематичною для деяких користувачів: невдала спроба з’єднання також призводить до невдачі запиту на початку черги запитів, що може призвести до несподіваних відмов запитів у певних сценаріях. Крім того, якщо в пулі є відкладене з’єднання, яке ніколи не стане доступним, наприклад, через несправність сервера або проблеми з мережею, нові вхідні запити, пов’язані з ним, також гальмуватимуться і можуть завершитися в тайм-ауті.

У .NET 7.0 було впроваджено наступні зміни для усунення цих проблем:

1. Невдала спроба з’єднання може призвести до невдачі лише того запиту, який її ініціював, але ніколи не може призвести до невдачі запиту, який не пов’язаний з нею. Якщо початковий запит було оброблено до того моменту, коли з’єднання було розірвано, то помилка з’єднання ігнорується (dotnet/runtime#62935).

 

2. Якщо запит ініціює нове з’єднання, але потім обробляється іншим з’єднанням з пулу, нова спроба з’єднання буде автоматично завершена через короткий проміжок часу, незалежно від ConnectTimeout. Завдяки цій зміні, зупинені з’єднання не будуть зупиняти непов’язані запити (dotnet/runtime#71785). Зауважте, що невдачі таких “відкинутих” спроб з’єднання відбуватимуться у фоновому режимі й ніколи не стануть відомі користувачеві, єдиний спосіб їх побачити – увімкнути телеметрію.

HttpHeaders зчитують безпеку потоку

Колекції HttpHeaders ніколи не були безпечними для потоків. Доступ до заголовка може призвести до лінивого розбору його значення, що може спричинити модифікацію базових структур даних.

До .NET 6 одночасне читання з колекції у більшості випадків було безпечним для потоків.

Починаючи з .NET 6, розбір заголовків став менше блокуватися, оскільки в ньому зникла внутрішня потреба. Через цю зміну з’явилося багато прикладів помилкового одночасного доступу користувачів до заголовків, наприклад, у gRPC (dotnet/runtime#55898),, NewRelic (newrelic/newrelic-dotnet-agent#803) або навіть у самому HttpClient (dotnet/runtime#65379). Порушення безпеки потоків у .NET 6 може призвести до дублювання/спотворення значень заголовків або генерування різних винятків під час зчитування/доступу до заголовків.

.NET 7 робить поведінку заголовків більш інтуїтивно зрозумілою. Колекція HttpHeaders тепер відповідає гарантіям потокової безпеки Словника:

Колекція може підтримувати декілька читачів одночасно, якщо її не модифіковано. У рідкісних випадках, коли перерахування конкурує з доступом на запис, колекція повинна бути заблокована протягом усього перерахування. Щоб дозволити доступ до колекції декільком потокам для читання і запису, потрібно реалізувати власну синхронізацію.

Цього було досягнуто наступними змінами:

“Читання з перевіркою” недійсного значення не призводить до видалення недійсного значення - dotnet/runtime#67833 

 

Одночасне читання є безпечним для потоків – dotnet/runtime#68115.

Виявлення помилок протоколів HTTP/2 і HTTP/3

Протоколи HTTP/2 і HTTP/3 визначають коди помилок на рівні протоколу у RFC 7540, розділ 7 і RFC 9114, розділ 8.1, наприклад, REFUSED_STREAM (0×7) в HTTP/2 або H3_EXCESSIVE_LOAD (0×0107) в HTTP/3. На відміну від кодів HTTP-статусу, це низькорівнева інформація про помилки, яка не є важливою для більшості користувачів HttpClient, але вона допомагає в розширених сценаріях HTTP/2 або HTTP/3, зокрема grpc-dotnet, де розпізнавання помилок протоколу є життєво важливим для реалізації повторних спроб клієнта.

Було створено нове виключення HttpProtocolException для зберігання коду помилки на рівні протоколу у його властивості ErrorCode.

При безпосередньому виклику HttpClient, HttpProtocolException може бути внутрішнім виключенням HttpRequestException:

При роботі з відповіддю потоку HttpContent він передається безпосередньо:

HTTP/3

Підтримка HTTP/3 у HttpClient вже була реалізована у попередній версії .NET, тому основні зусилля у цій сфері було зосереджено на базовому System.Net.Quic. Попри це, було внесено декілька виправлень та змін у .NET 7.

 

Найважливішою зміною є те, що HTTP/3 тепер увімкнено за замовчуванням (dotnet/runtime#73153). Це не означає, що відтепер всі HTTP-запити будуть віддавати перевагу HTTP/3, але в певних випадках вони можуть оновитися до нього. Щоб це сталося, запит повинен вибрати оновлення версії за допомогою HttpRequestMessage.VersionPolicy, встановленого в RequestVersionOrHigher. Потім, якщо сервер оголосить HTTP/3 повноваження в заголовку Alt-Svc, HttpClient буде використовувати його для подальших запитів, див. RFC 9114, розділ 3.1.1.

Ось декілька інших цікавих змін:

1. Телеметрія HTTP була розширена на HTTP/3 – dotnet/runtime#40896.

2. Покращено деталі винятків у випадку неможливості встановлення з’єднання QUIC – dotnet/runtime#70949.

 

3. Виправлено правильне використання заголовка Host для ідентифікації імені сервера (SNI) – dotnet/runtime#57169.

QUIC

QUIC – це новий протокол транспортного рівня. Нещодавно його було стандартизовано у RFC 9000. Він використовує UDP як базовий протокол і є безпечним за своєю суттю, оскільки вимагає використання TLS 1.3, див. RFC 9001. Ще одна цікава відмінність від відомих транспортних протоколів, таких як TCP і UDP, полягає в тому, що він має вбудоване потокове мультиплексування на транспортному рівні. Це дозволяє мати декілька паралельних, незалежних потоків даних, які не впливають один на одного.

Сам по собі QUIC не визначає ніякої семантики для даних, що обмінюються, оскільки це транспортний протокол. Він скоріше використовується в протоколах прикладного рівня, наприклад, в HTTP/3 або в SMB over QUIC. Він також може бути використаний для будь-якого користувацького протоколу.

 

Протокол має багато переваг над TCP з TLS. Наприклад, швидше встановлення з’єднання, оскільки він не вимагає стільки обходів, як TCP з TLS. Або уникнення проблеми блокування головної лінії, коли один втрачений пакет не блокує дані всіх інших потоків. З іншого боку, використання QUIC має свої недоліки. Оскільки це новий протокол, його впровадження все ще триває і є обмеженим. Крім того, трафік QUIC може навіть блокуватися деякими мережевими компонентами.

QUIC в .NET

Реалізацію QUIC в .NET 5 представлено в бібліотеці System.Net.Quic. Однак до цього часу бібліотека була суто внутрішньою і слугувала лише для власної реалізації HTTP/3. З виходом .NET 7 бібліотека стає загальнодоступною і доступні її API. Оскільки в цій версії API використовувалися лише HttpClient та Kestrel, було вирішено залишити їх у вигляді функції попереднього перегляду.

Це дає можливість доопрацювати API в наступному релізі, перш ніж він набуде остаточного вигляду.

 

З точки зору реалізації, System.Net.Quic залежить від MsQuic, нативної реалізації протоколу QUIC. В результаті, підтримка платформи System.Net.Quic і залежності успадковані від MsQuic і задокументовані в залежності від платформи HTTP/3. Коротко кажучи, бібліотека MsQuic постачається як частина .NET для Windows. Для Linux libmsquic необхідно встановити вручну за допомогою відповідного менеджера пакунків. Для інших платформ, як і раніше, можна зібрати MsQuic вручну, чи то для SChannel, чи то для OpenSSL, і використовувати її з System.Net.Quic.

Огляд API

System.Net.Quic містить три основні класи, які дозволяють використовувати протокол:

QuicListener – клас на стороні сервера для приймання вхідних з’єднань.

QuicConnection – QUIC з’єднання, що відповідає RFC 9000 Section 5.

QuicStream – потік QUIC, що відповідає RFC 9000 Section 2.

Але перед будь-яким використанням цих класів користувацький код повинен перевірити, чи підтримується QUIC, оскільки libmsquic може бути відсутнім, або TLS 1.3 може не підтримуватися. Для цього і QuicListener, і QuicConnection мають статичну властивість IsSupported:

 

Зауважте, що наразі обидві ці властивості синхронізовані й показуватимуть однакове значення, але це може змінитися у майбутньому. Тому рекомендується перевірити QuicListener.IsSupported для серверних сценаріїв і QuicConnection.IsSupported для клієнтських.

QuicListener

 

QuicListener являє собою клас на стороні сервера, який приймає вхідні з’єднання від клієнтів. Слухач створюється і запускається за допомогою статичного методу QuicListener.ListenAsync. Метод приймає екземпляр класу QuicListenerOptions з усіма налаштуваннями, необхідними для запуску слухача і приймання вхідних з’єднань. Після цього слухач готовий роздавати з’єднання через AcceptConnectionAsync. З’єднання, що повертаються цим методом, завжди повністю з’єднані, що означає, що рукостискання TLS завершено і з’єднання готове до використання. Нарешті, щоб припинити прослуховування і звільнити всі ресурси, необхідно викликати DisposeAsync.

 

Приклад використання QuicListener:

 

Більш детальну інформацію про те, як було розроблено цей клас, можна знайти у QuicListener API Proposal (dotnet/runtime#67560).

QuicConnection

QuicConnection – це клас, який використовується як для серверних, так і для клієнтських QUIC-з’єднань. З’єднання на стороні сервера створюються внутрішньо слухачем і роздаються через QuicListener.AcceptConnectionAsync. Клієнтські з’єднання повинні бути відкриті й підключені до сервера. Як і у випадку зі слухачем, існує статичний метод QuicConnection.ConnectAsync, який створює та встановлює з’єднання. Він приймає екземпляр класу QuicClientConnectionOptions, аналогічного класу QuicServerConnectionOptions. Після цього робота зі з’єднанням не відрізняється між клієнтом і сервером. Він може відкривати вихідні потоки й приймати вхідні. Він також надає властивості з інформацією про з’єднання, такі як LocalEndPoint, RemoteEndPoint або RemoteCertificate.

Після завершення роботи зі з’єднанням його потрібно закрити та скинути. Протокол QUIC вимагає використання коду програмного рівня для негайного закриття, див. RFC 9000, розділ 10.2. Для цього можна викликати CloseAsync з кодом програмного рівня або, якщо ні, DisposeAsync використає код, наданий у QuicConnectionOptions.DefaultCloseErrorCode. У будь-якому випадку, DisposeAsync має бути викликано наприкінці роботи зі з’єднанням, щоб повністю звільнити всі пов’язані з ним ресурси.

Приклад використання QuicConnection:

 

Більш детально про те, як був розроблений цей клас, можна прочитати в QuicConnection API Proposal (dotnet/runtime#68902).

QuicStream

 

QuicStream – це тип, який використовується для надсилання та отримання даних у протоколі QUIC. Він походить від звичайного потоку і може використовуватися як такий, але він також пропонує кілька особливостей, які є специфічними для протоколу QUIC. По-перше, потік QUIC може бути однонапрямний або двонапрямний, див. RFC 9000, розділ 2.1. Двонапрямний потік може надсилати та отримувати дані з обох сторін, тоді як однонапрямний потік може тільки писати зі сторони, що ініціює та читати зі сторони, що приймає. Кожен одноранговий комп’ютер може обмежити кількість одночасних потоків кожного типу, див. QuicConnectionOptions.MaxInboundBidirectionalStreams та QuicConnectionOptions.MaxInboundUnidirectionalStreams.

Ще однією особливістю потоку QUIC є можливість явно закрити сторону запису посеред роботи з потоком, див. перевантаження CompleteWrites або WriteAsync-system-boolean-system-threading-cancellationtoken)) з аргументом completeWrites. Закриття сторони запису дає змогу одноранговій системі знати, що дані більше не надходитимуть, проте вона може продовжувати надсилати (у випадку двонапрямного потоку). Це корисно в таких сценаріях, як обмін HTTP-запитами/відповідями, коли клієнт надсилає запит і закриває сторону запису, щоб повідомити серверу, що на цьому вміст запиту закінчився. Сервер все ще може відправити відповідь після цього, але знає, що більше ніяких даних від клієнта не надійде. У випадку помилкових ситуацій можна перервати потік як на стороні запису, так і на стороні читання, див. розділ Переривання. Поведінка окремих методів для кожного типу потоку підсумована у наступній таблиці (зауважте, що і клієнт, і сервер можуть відкривати й приймати потоки):

 

На додаток до цих методів, QuicStream пропонує дві спеціалізовані властивості для отримання сповіщень про закриття потоку для читання або запису: ReadsClosed та WritesClosed. Обидві властивості повертають завдання, яке завершується закриттям відповідної сторони потоку, незалежно від того, чи було воно успішне, чи перериване, в останньому випадку завдання буде містити відповідний виняток. Ці властивості корисні, коли користувацькому коду потрібно знати про закриття сторони потоку без виклику ReadAsync або WriteAsync.

 

Нарешті, коли робота з потоком завершена, його потрібно вилучити за допомогою DisposeAsync. Програма переконається, що сторона читання та/або запису – залежно від типу потоку – закрита. Якщо потік не було прочитано належним чином до кінця, програма видасть еквівалент Abort(QuicAbortDirection.Read). Однак, якщо потік не було закрито на стороні запису, він буде поступово закритий, як це було б у випадку з CompleteWrites. Причина такої різниці полягає в тому, щоб переконатися, що сценарії, які працюють зі звичайним потоком, поводяться очікувано і ведуть до успішного завершення. Розглянемо наступний приклад:

Приклад використання QuicStream у клієнтському сценарії:

І приклад використання QuicStream у серверному сценарії:

Більш детально про те, як був розроблений цей клас, можна прочитати в QuicStream API Proposal (dotnet/runtime#69675).

Безпека

Узгодження API

Автентифікація Windows – це загальний термін для позначення різних технологій, що використовуються на підприємствах для автентифікації користувачів і програм за допомогою центрального органу, зазвичай контролера домену. Вона уможливлює такі сценарії, як єдиний вхід до служб електронної пошти або програм інтрамережі. Основними технологіями, що використовуються для автентифікації, є Kerberos, NTLM і протокол Negotiate, де для конкретного сценарію автентифікації вибирається найбільш відповідна технологія.

До .NET 7 автентифікація Windows була доступна у високорівневих API, таких як HttpClient (схеми автентифікації Negotiate і NTLM), SmtpClient (схеми автентифікації GSSAPI й  NTLM), NegotiateStream, ASP.NET Core і у клієнтських бібліотеках SQL Server. Хоча вона охоплює більшість сценаріїв для кінцевих користувачів, проте обмежує можливості авторів бібліотек. Інші бібліотеки, такі як клієнт Npgsql PostgreSQL, MailKit, клієнт Apache Kudu та інші, повинні були вдаватися до різних хитрощів, щоб реалізувати ті ж схеми автентифікації для низькорівневих протоколів, які не були побудовані на HTTP або інших доступних високорівневих будівельних блоках.