Ориентация главы
«Добавить аутентификацию» в HTTP — задача выбора из трёх семейств схем.Basic: Authorization: Basic base64(用户:密码) (пользователь:пароль) — открытый текст (приемлемая форма только под TLS), по строке на каждом конце.Bearer: Authorization: Bearer <令牌> (токен) — носитель эпохи OAuth2/JWT; клиент только несёт его.Digest: сервер выдаёт nonce-вызов, клиент отвечает H(用户:域:密码) (пользователь:область:пароль) — протокол вызов-ответ, где пароль не выходит в сеть, со встроенной защитой от повторов (nonce + счётчик nc); сервер ведёт жизненный цикл nonce, таблицу повторов и квитанцию Authentication-Info. xhttp реализует оба конца всех трёх схем как структурированные API: построение на клиенте (xrtHttpBasicWrite/BearerWrite/ответ Digest), чтение на сервере (три состояния xrtHttpServerRequestBasicAuth/BearerAuth/DigestAuth), полная цепочка проверки Digest (четыре состояния verify у xrtHttpDigestVerify + потокобезопасная таблица повторов). Отсюда собирается middleware авторизации главы 105.
106.1引入
Три формы развёртывания, каждая на своём месте. Инструменты интрасети: Basic + TLS достаточно — по строке кода на каждом конце; пароль открытый текст, но туннель шифрованный. API-платформы: Bearer — выдача/обновление токенов дело платформы, сервер только проверяет токен; «аутентификация» клиента — просто принести его. Недоверенная среда или протокольное соответствие (защита пароля в семантике HTTP): Digest — дайджесты паролей на сервере, вызов-ответ защищает от повторов; цена — сервер поддерживает состояние nonce и повторов.Цена неправильного выбора: Basic поверх открытого текста HTTP равносилен трансляции пароля;.stateless Bearer на платформе, где нужно отзывать токены, равносилен невозможности отзыва; использовать Digest как «более безопасный Basic», не управляя истечением nonce, — окно повторов открыто как прежде.
Единая форма auth-API заслуживает внимания: всё идёт через три состояния XHTTP_NEXT_END/ITEM/ERROR (отсутствует/действителен/ошибка) — тот же контракт, что у структурированного чтения полей (главы 92/104); построение вызова «дописывает» (AddChallenge — сосуществование многих схем), квитанция «устанавливает» (SetDigestInfo — единственное поле) — многозначная/единственная семантика RFC ложится на форму API.
106.2概念
Basic: по строке на каждом конце и базовая линия открытого текста
Клиент xrtHttpBasicWrite(用户, 密码, 输出, 容量, &长度) (пользователь, пароль, вывод, ёмкость, &длина) производит значение поля Basic <base64>; сервер xrtHttpServerRequestBasicAuth(请求, 解码缓冲, 容量, &长度, &描述符) (запрос, буфер декодирования, ёмкость, &длина, &дескриптор) — результаты декодирования Basic и Digest заимствуют буфер вызывающего (короткий буфер публикует только точную нужную длину; ошибка очищает дескриптор, оставляя длину и тело вызывающего нетронутыми — половину открытого текста вперёд не фиксируем). Дисциплина базовой линии: Basic только под TLS; Basic поверх открытого текста http — авария конфигурации.
Bearer: ношение токенов и вызовы
Клиент xrtHttpBearerWrite производит Bearer <token>; сервер xrtHttpServerRequestBearerAuth берёт представление токена (заимствует снимок запроса — сам токен принадлежит вашему слою проверки: локальная проверка подписи, запрос к базе, вызов сервиса авторизации).Граница безопасности Bearer — в управлении токенами: выдача, истечение, отзыв, области минимальных прав — протокольный слой лишь носитель; вход построения вызова 401 (WWW-Authenticate: Bearer error="invalid_token") ведает синтаксисом, политика — приложению.
Digest: вызов, ответ и четыре состояния проверки
Точная семантика четырёх состояний: XHTTP_DIGEST_VERIFY_VALID (подпись и доказательство верны), _STALE (подпись и доказательство верны, но nonce истёк — клиенту следует повторить со свежим nonce; это не провал), _INVALID (доказательство неверно — отвергнуть), _ERROR (ошибка ввода/структуры).Только VALID фиксирует монотонный nc — ранняя фиксация позволила бы ошибочному доказательству захватить счёт легитимного клиента (таблица повторов отравлена). Управление nonce: встроено в вход по умолчанию; собственные nonce или вращающееся кольцо ключей — комбинация двух уровней xrtHttpDigestProofVerify+NonceVerify. Квитанция Authentication-Info (взаимная аутентификация rspauth + вращение nextnonce) генерируется после успешной проверки из того же контекста доказательства — полный замкнутый цикл вызова и ответа.
Формы развёртывания защиты от повторов
Один процесс: xrtHttpDigestReplayCheck — потокобезопасная таблица повторов с жёсткой ёмкостью; много процессов/распределённо: xrtHttpDigestReplayKey генерирует канонический ключ, общее хранилище делает атомарное обновление максимума с истечением (nc — монотонный счётчик — max-обновление с семантикой CAS). Окно повторов = срок nonce; срок — параметр баланса безопасности и опыта.
Разбор вызовов на клиенте
xrtHttpChallengeNext итерирует многосхемные вызовы в ответе 401 (Digest realm=..., Basic ... смешанные объявления) — клиент выбирает схему по возможностям: кэш пароля — Digest, токен — Bearer, откат — Basic. Слой политики автоматического повтора (глава 100), получив 401, собирается отсюда.
106.3示例
Первая полная программа: по-схемный разбор вызова сервера
Программа ниже из examples/http/auth — структурированное чтение многосхемного вызова:
#include <stdio.h> #include <xhttp.h> /* 逐项读取服务端返回的认证 challenge。 */ int main(void) { xstrview Value = XRT_STR_LITERAL( "Digest realm=\"api\", Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" ); xhttpauth Challenge; xhttpnext Next; size_t iOffset = 0; while ( (Next = xrtHttpChallengeNext( Value, &iOffset, &Challenge )) == XHTTP_NEXT_ITEM ) { printf("%.*s\n", (int)Challenge.Scheme.Size, Challenge.Scheme.Data); } return (Next == XHTTP_NEXT_END) ? 0 : 1; }
Что только что произошло. (1) Ввод — смешанный вызов Digest realm="api", Basic QWxhZGRpbjpvcGVuIHNlc2VtZQ== — два объявления схем в одном значении WWW-Authenticate. (2) xrtHttpChallengeNext итерирует по смещению, выдавая каждый xhttpauth (представление Scheme + параметры) — по позиции на Digest и Basic; та же форма, что итерация семейства полей главы 92 (домен запятых, смещение в руках). (3) Отправная точка клиентской политики: сканировать по порядку, сопоставлять по возможностям — «что объявил сервер» — структурированный факт, а не угадывание по строкам. Параметры Digest (realm/nonce/qop) продолжают разбираться параметрическим слоем главы 92.
Вторая полная программа: кратчайший путь обоих концов Basic
Вторая программа из examples/http/auth_basic — построение на клиенте:
#include <stdio.h> #include <xhttp.h> /* 构建 Basic Authorization 字段值。 */ int main(void) { char Value[128]; size_t iSize; if ( !xrtHttpBasicWrite( XRT_STR_LITERAL("Aladdin"), XRT_STR_LITERAL("open sesame"), Value, sizeof(Value), &iSize ) ) { return 1; } printf("%.*s\n", (int)iSize, Value); return 0; }
Что только что произошло. (1) xrtHttpBasicWrite("Aladdin", "open sesame") производит полное значение поля одним вызовом — Base64 (глава 28) встроен, атомарность ёмкости как по всей библиотеке. (2) Вывод — ровно каноническое примерное значение RFC 7617 — стандартные векторы снова якорят корректность реализации. (3) Серверная противоположность xrtHttpServerRequestBasicAuth декодирует в буфер вызывающего — четыре вызова на оба конца, и это весь Basic. Семья сопроводительных образцов: auth_bearer (построение токена), auth_digest (полный вызов-ответ), auth_digest_client/auth_digest_session (сессии и переиспользование nonce), auth_digest_nonce/_replay (nonce и повторы), digest_sha2 (семейство алгоритмов SHA-2) — у каждого отрезка Digest свой образец.
106.4契约
- Три состояния едины: семейство чтения
END(отсутствует)/ITEM(действителен)/ERROR(дублирующееся поле, нелегальный синтаксис, несовпадение схемы, провал декодирования); дублирующийся Authorization — протокольная ошибка Header. - Граница заимствования: общие результаты и Bearer заимствуют снимок запроса; результаты декодирования Basic и Digest заимствуют буфер вызывающего; короткий буфер публикует точную длину, ошибка не трогает состояние вызывающего.
- Базовая линия Basic: открытый текст (base64 не шифрование); только под TLS; по одному вызову API на каждый конец.
- Граница Bearer: проверка/отзыв/права токена — прикладному слою; построение синтаксиса вызова — слою аутентификации.
- Четыре состояния Digest: VALID/STALE (подпись верна, nonce истёк — повторяемо)/INVALID (доказательство неверно)/ERROR; только VALID фиксирует nc.
- Управление nonce: по умолчанию встроено; собственное — комбинация ProofVerify+NonceVerify; срок = окно повторов.
- Таблица повторов: один процесс ReplayCheck (потокобезопасная, жёсткий потолок); распределённо ReplayKey + общее хранилище с атомарным max.
- Вызов/квитанция: Add дописывает многие схемы; Set ставит единственный Authentication-Info (rspauth+nextnonce); статус 401/407 автоматически не меняется — политика приложению.
- Обрезка:
HTTP_AUTH/HTTP_DIGESTнезависимы; семейство алгоритмов SHA-2 опционально.
Типичные ошибки
Симптом: захват пакетов видит Authorization: Basic <base64(密码)> (пароль) — пароль фактически транслируется.
Причина: base64 — кодирование, не шифрование. Вся безопасность Basic — от транспортного слоя; под TLS он уровня «защита от подглядывания через плечо».
/* http:// интрасетевой адрес + Basic: в захвате/журнале прокси интрасети сплошные пароли открытым текстом */ xrtHttpRequestSetBasicAuth(pReq, User, Pass); send_http(url); /* без TLS */
/* Basic идёт только с https */ if ( !url_is_https(pUrl) ) { return error("basic auth requires TLS"); } xrtHttpRequestSetBasicAuth(pReq, User, Pass);
Симптом: легитимный клиент с истёкшим nonce получает 401 и вылетает из логина — опыт ломается; или наоборот, INVALID принят за STALE с разрешением повтора — брутфорс-доказательства крутятся бесконечно.
Причина: четыре состояния обязаны обрабатываться по веткам: STALE — «начните заново со свежим nonce» (клиент уже имеет H(A1) и повторяет бесшовно), INVALID — «доказательство просто неверно» (отвергнуть и посчитать).
switch ( xrtHttpDigestVerify(&V) ) { default: reject_and_logout(); /* STALE тоже принят за провал */ }
xhttpdigestverifycheck R = xrtHttpDigestVerify(&V, Key, Ctx, Now, Life, Skew, NULL); if ( R == XHTTP_DIGEST_VERIFY_VALID ) { accept(); } else if ( R == XHTTP_DIGEST_VERIFY_STALE ) { challenge_with_fresh_nonce(); /* клиент повторит со свежим nonce */ } else { reject_and_count(); /* INVALID/ERROR: отвергнуть + посчитать */ }
Симптом: счётчик таблицы повторов захвачен ошибочными доказательствами — следующий запрос легитимного клиента признаётся повтором и отвергается.
Причина: nc — монотонный счёт «запросов, уже израсходованных под этим nonce»; запрос INVALID не должен занимать номер — контракт ясно говорит «фиксирует только VALID».
verify_partial(&V); /* проверена только подпись */ replay_commit(nonce, nc); /* занять номер сперва - ошибся или нет */ if ( full_verify(&V) ) { accept(); }
if ( xrtHttpDigestVerify(&V, Key, Ctx, Now, Life, Skew, NULL) == XHTTP_DIGEST_VERIFY_VALID ) { replay_commit(nonce, nc); /* номер - только при полностью верном доказательстве */ accept(); }
Практические задания
Постройте вызов с Digest+Basic+Bearer; клиент разбирает и выбирает схему по конфигурации (есть пароль/есть токен/ничего), печатая выбор. Критерии приёмки: все три конфигурации выбирают верную схему; параметры вызова (realm/nonce) читаются как структура.
Сервер (сервер + middleware главы 104) выдаёт вызов Digest; клиент разбирает nonce, считает ответ, отправляет; серверный DigestVerify идёт по четырём веткам. Повторите запрос, проверьте рост nc. Критерии приёмки: первый запрос VALID; повтор того же nonce отвергнут таблицей повторов; истёкший nonce проходит через STALE, и повтор успешен.
Middleware шлюза Bearer: поиск токена в локальном кэше (Map + TTL главы 18), при промахе — в сервис авторизации (симуляция); список отзыва поддерживается (отзыв инвалидирует кэш). Критерии приёмки: действительный токен проходит, недействительный 401, отозванный — 401 немедленно; путь попадания в кэш без вызовов бэкенда (проверка подсчётом); токены никогда не попадают в журнал (дисциплина главы 77).
Итоги
| Тема | Кратко |
|---|---|
| Три схемы | Basic (открытый текст + базовая линия TLS) / Bearer (носитель токена) / Digest (вызов-ответ) |
| Чтение трёх состояний | END отсутствует / ITEM действителен / ERROR дубль·нелегальность·несовпадение·провал декодирования |
| Граница заимствования | общие и Bearer заимствуют запрос; декодирование Basic/Digest заимствует буфер вызывающего (короткий буфер публикует точную длину) |
| Построение вызова | AddChallenge дописывает многие схемы; входы Basic/Bearer/Digest каждый полностью валидируются |
| Четыре состояния Digest | VALID / STALE (nonce истёк, повторяемо) / INVALID (доказательство неверно) / ERROR |
| Дисциплина nc | только VALID фиксирует монотонный счёт - ранняя фиксация отдаёт номера ошибочным доказательствам |
| Nonce | по умолчанию встроен; собственный ProofVerify+NonceVerify; срок = окно повторов |
| Таблица повторов | один процесс ReplayCheck; распределённо ReplayKey + атомарный max общего хранилища |
| Квитанция | после VALID RspAuth генерирует, SetDigestInfo ставит единственный Authentication-Info |
| Клиент | ChallengeNext итерирует многие схемы и выбирает по возможностям; H(A1) кэшируется локально |
📖 Справочники API в этой главе:xhttp-http_auth (64 записей)xhttp-http_server (75 записей)xhttp-http_digest (13 записей)
Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки