--- title: Signing description: Публичный API подписи запросов. sources: - eazy_sdk.request.signatures api_fingerprint: "sha256:35cabc99ddaae562cde32a34de5215588540d6b91c7fa14d9c58fc1db35d9e82" --- ## HMAC ```python hmac_sha256(*, key, base, output) ``` ## Данные подписи ```python join(*parts, separator=b"", prefix=b"", suffix=b"") method() path() target() query(...) body_digest(...) canonical_json(...) ``` ## Результат ```python header_output(name, encoding="hex") query_output(name, encoding="hex") cookie_output(name, encoding="hex") body_output(name, encoding="hex") ``` Ключ задаётся через `SigningKeyRequirement(name)`. `join(...)` объединяет компоненты с явным separator. Доступные projections включают `header(...)`, `query(...)`, `canonical_json(...)`, `wire_body()` и `previous_signature(...)`. Output может записать подпись в header, query, cookie или body. `encoding` принимает `hex`, `base64` или `raw`; `position` фиксирует место для exact wire API. Подпись объявляется **по месту объявления**: параметром `signing=` операции или атрибутом `signing` роутера/миксина сервиса, наследуемым по MRO. Адресных (host/path) правил подписи нет: глядя на операцию, всегда видно, подписана она или нет, а опечатка в правиле не может оставить запрос молча неподписанным. Если весь сервис подписывается одинаково, правило пишется один раз: ```python from dataclasses import dataclass from eazy_sdk import AsyncApi, Http, HttpOperation, JsonField, op from eazy_sdk.request import body_digest, header_output, hmac_sha256 class PaymentsService: base_url = "https://payments.example" signing = (hmac_sha256(key=KEY, base=body_digest(), output=header_output("X-Signature")),) signed = True # операция без подписи отвергается при компиляции @dataclass(frozen=True, slots=True, kw_only=True) class Charge(HttpOperation[Receipt]): __http__ = Http.post("/charge") # наследует подпись сервиса account: JsonField[str] @dataclass(frozen=True, slots=True, kw_only=True) class Refund(HttpOperation[Receipt]): __http__ = Http.post("/refund", signing=(REFUND_SIGNATURE,)) account: JsonField[str] class PaymentsApi(PaymentsService, AsyncApi): charge = op(Charge) refund = op(Refund) ``` `signing=` на операции **заменяет** сервисное правило целиком, а не дополняет его; `signing=()` снимает подпись и вместе с `signed = True` даёт ошибку при компиляции. Полная таблица слияния приведена в {doc}`мультисервисном SDK `. База подписи считается от тех же байтов, которые уйдут, в том числе когда тело зашифровано целиком, и кодируется той же `JsonPolicy`, что и тело (см. {doc}`Представление запроса `). `Identity.key_provider` является callable `(SigningKeyRequirement) -> SigningKey`. Secrets отсутствуют в arguments операции и redacted в `repr`.