JWT: структура и валидация
JWT — самодостаточный токен из трёх частей через точку: header.payload.signature (каждая — base64url). В payload лежат claims (sub, iss, aud, exp, роли), а signature — подпись (HMAC на секрете или RSA/EC на паре ключей).
Resource server (ваше API) проверяет подпись, issuer (iss), audience (aud) и срок (exp), а затем маппит claims в authorities. Важно: payload ПОДПИСАН, но НЕ зашифрован — его может прочитать любой. Поэтому секреты в JWT не кладут.
Классическая атака — подмена алгоритма в header. alg:none: злоумышленник ставит заголовок "alg":"none" и шлёт токен без подписи; наивная библиотека «проверяет» подпись none и принимает токен. Algorithm confusion: токен, который сервер ждёт подписанным RSA (RS256, проверка публичным ключом), злоумышленник переподписывает HMAC (HS256), используя публичный RSA-ключ как HMAC-секрет. Защита одна: сервер должен жёстко фиксировать ожидаемый алгоритм и не доверять полю alg из токена. В Spring Security это закрыто из коробки (NimbusJwtDecoder с заданным алгоритмом) — главное не отключать и не «чинить» проверку вручную.
Отзыв и refresh
Цена stateless-самодостаточности: выданный JWT нельзя «отозвать» до истечения — сервер его не хранит и не отслеживает. Если токен украли, он валиден до exp.
Поэтому на практике используют короткоживущий access-токен (минуты) плюс долгоживущий refresh-токен, который хранится и может быть отозван на сервере; access обновляют по refresh. Для немедленного отзыва ведут blacklist/версионирование — это уже добавляет состояние, размывая «чистый stateless».
OAuth2: роли и flow
OAuth2 — протокол делегированного доступа с тремя ролями: authorization server выдаёт токены, resource server защищает API и проверяет токены (это типичная роль Spring-приложения, oauth2ResourceServer), client получает токен от имени пользователя.
Базовый сценарий — authorization code flow: пользователь логинится у authorization server, приложение получает короткоживущий code, меняет его на токены на бэкенде. Так пароль пользователя не проходит через ваше приложение.

OIDC
OpenID Connect (OIDC) — слой identity поверх OAuth2. Если чистый OAuth2 — про ДОСТУП (authorization), то OIDC добавляет id_token (тоже JWT) с информацией о пользователе и нужен для ЛОГИНА (authentication) через внешнего провайдера (Google, Keycloak, Okta).
Различайте: access_token — для вызова API, id_token — чтобы узнать, кто вошёл. Spring поддерживает обе роли (oauth2Login для клиента, oauth2ResourceServer для API).
Method security
Кроме авторизации по URL, права проверяют на уровне методов: @EnableMethodSecurity включает @PreAuthorize/@PostAuthorize (с выражениями SpEL) и @Secured. Это полезно, когда один сервисный метод вызывается из разных точек входа (REST, очередь, шедулер) — защита в одном месте.
Method security работает через AOP-прокси, поэтому наследует ту же ловушку self-invocation (вызов через this мимо прокси не проверяется). И не ограничивайтесь ролями — часто нужна проверка владения объектом (ownership).
@PreAuthorize("hasAuthority('ORDER_READ') and @acl.canReadOrder(#id)") // роль + ownership
OrderDto getOrder(long id) { /* ... */ }
Как отвечать на собеседовании
Опишите JWT (header.payload.signature, подписан но не зашифрован → без секретов; сервер проверяет подпись/iss/aud/exp) и главную проблему — отзыв (короткий access + refresh). Различите роли OAuth2 (resource server/client/authorization server) и authorization code flow, и OIDC как identity-слой (id_token, логин) против OAuth2 (доступ). Завершите method security через AOP (@PreAuthorize, self-invocation, ownership, а не только роли).