Как использовать аутентификацию токенов доступа
Дата обновления перевода 2024-07-27
Как использовать аутентификацию токенов доступа
Токены доступа или токены API часто используются как механизм аутентификации в контексте API. Токен доступа - это строка, получення во время аутентификации (используя приложение или сервер авторизации). Роль токена доступа заключается в верификации личности пользователя и получения согласия до выпуска токена.
Токены доступа могут быть любого вида, например, непрозрачными строками, веб-токенами JSON (JWT) или SAML2 (XML-структурами). Пожалуйста, прочтите RFC6750: Фреймворк авторизации OAuth 2.0: Использование токена предъявителя, чтобы узнать детали.
Использование аутентификатора токена доступа
Это руководство предполагает, что вы настроили безопасность и создали объект пользователя в вашем приложении. Следуйте основному руководству по безопасности, если это еще не так.
1) Сконфигурируйте аутентификатор токена доступа
Чтобы использовать аутентификатор токена доступа, вы должны сконфигурировать
token_handler
. Обработчик токенов получает токен из запроса и возврращает
правильный идентификатор пользователя. Чтобы получить идентификатор пользователя,
реализации может быть нужно загрузить и валидировать токен (например, аннулирование,
срок действия, цифровую подпись и т.д.).
1 2 3 4 5 6
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
Этот обработчик должен реализовывать AccessTokenHandlerInterface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// src/Security/AccessTokenHandler.php
namespace App\Security;
use App\Repository\AccessTokenRepository;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
class AccessTokenHandler implements AccessTokenHandlerInterface
{
public function __construct(
private AccessTokenRepository $repository
) {
}
public function getUserBadgeFrom(string $accessToken): UserBadge
{
// например, запрос к базе данных "access token", чтобы искать этот токен
$accessToken = $this->repository->findOneByValue($token);
if (null === $accessToken || !$accessToken->isValid()) {
throw new BadCredentialsException('Invalid credentials.');
}
// и вернуть объект UserBadge, содержащий идентификатор пользователя из найденного токена
return new UserBadge($accessToken->getUserId());
}
}
Аутентификатор токена доступа будет использовать возвращенный идентификатор пользователя, чтобы загрузить пользователя, используя поставщика пользователей .
Caution
Важно проверить, валиден ли токен. Например, пример выше верифицирует, не
истек ли у токена срок действия. При использовании самодостаточных токенов
доступа, таких как JWT, обработчик должен верифицировать цифровую подпись
и понять все утверждения, особенно sub
, iat
, nbf
и exp
.
2) Сконфигурируйте экстрактор токенов (необязательно)
Теперь приложение готово к обработке входящих токенов. Экстрактор токенов извлекает токен из запроса (например, заголовка или тела запроса).
По умолчанию, токен доступа читается из параметра заголовка запроса Authorization
со схемой Bearer
(например, Authorization: Bearer the-token-value
).
Symfony предоставляет другие экстракторы, в соответствии с RFC6750:
header
(по умолчанию)-
Токен отправляется через заголовок запроса. Обычно -
Authorization
со схемойBearer
. query_string
-
Токен является частью строки запроса. Обычно -
access_token
. request_body
-
Токен является частью тела запроса во время запроса POST. Обычно -
access_token
.
Caution
Из-за уязвимости безопасности, связанной с методом URI, включая высокую
вероятность, что будет произведена запись лога URL или тела запроса, которые
содержат токен доступа, методы query_string
и request_body
НЕ ДОЛЖНЫ
быть использованы, кроме случаев, когда невозможно переместить токен доступа в
поле заголовка запроса.
Вы также можете создать пользовательский экстрактор. Класс должен реализовывать AccessTokenExtractorInterface.
1 2 3 4 5 6 7 8 9 10 11 12
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
# использовать другой встроенный экстрактор
token_extractors: request_body
# или предоставить ID сервиса пользовательского экстрактора
token_extractors: 'App\Security\CustomTokenExtractor'
Возможно установить несколько экстракторов. В таком случае, порядок имеет значение: первый в списке вызывается первым.
1 2 3 4 5 6 7 8 9
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
token_extractors:
- 'header'
- 'App\Security\CustomTokenExtractor'
3) Отправьте запрос
Это все! Ваше приложение теперь может аутентифицировать входящие запросы, используя токен API.
Используя экстрактор заголовка по умолчанию, вы можете протестировать функцию, отправив запрос таким образом:
1 2
$ curl -H 'Authorization: Bearer an-accepted-token-value' \
https://localhost:8000/api/some-route
Настройка обработчика успеха
По умолчанию, запрос продолжается (например, запускается контроллер для маршрута).
Если вы хотите настроить обработку успеха, создайте собственного обработчика успеха,
создав класс, реализующий
AuthenticationSuccessHandlerInterface,
и сконфигурируйте ID сервиса как success_handler
:
1 2 3 4 5 6 7
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler: App\Security\AccessTokenHandler
success_handler: App\Security\Authentication\AuthenticationSuccessHandler
Tip
Если вы хотите настроить обработку неудачи по умолчанию, используйте опцию
failure_handler
и создайте класс, реализующий
AuthenticationFailureHandlerInterface.
Использование OpenID Connect (OIDC)
OpenID Connect (OIDC) - это третье поколение технологии OpenID, которое представляет собой RESTful HTTP API, использующий JSON в качестве формата данных. OpenID Connect - это уровень аутентификации на основе фреймворка авторизации OAuth 2.0. Он позволяет проверять личность конечного пользователя на основе аутентификации, выполненной сервером авторизации.
1) Сконфигурируйте the OidcUserInfoTokenHandler
OidcUserInfoTokenHandler
требует пакет ymfony/http-client
для
выполнения необходимых HTTP-запросов. Если вы еще не установили его, выполните эту команду:
1
$ composer require symfony/http-client
Symfony предоставляет общий OidcUserInfoTokenHandler
для вызова вашего сервера OIDC
и получения информации о пользователе:
1 2 3 4 5 6 7
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc_user_info: https://www.example.com/realms/demo/protocol/openid-connect/userinfo
Согласно спецификации OpenID Connect, в качестве идентификатора пользователя по умолчанию
используется утверждение sub
. Чтобы использовать другое утверждение, укажите его в
конфигурации:
1 2 3 4 5 6 7 8 9
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc_user_info:
claim: email
base_uri: https://www.example.com/realms/demo/protocol/openid-connect/userinfo
Обработчик токена oidc_user_info
автоматически создаёт HTTP-клиент с
указанным base_uri
. Если вы хотите использовать собственный клиент, вы можете
указать имя сервиса с помощью опции client
:
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc_user_info:
client: oidc.client
По умолчанию, OidcUserInfoTokenHandler
создаёт OidcUser
с утверждениями.
Чтобы создать собственный объект пользователя на основе утверждений, вы должны
создать собственный UserProvider:
1 2 3 4 5 6 7 8 9 10
// src/Security/Core/User/OidcUserProvider.php
use Symfony\Component\Security\Core\User\AttributesBasedUserProviderInterface;
class OidcUserProvider implements AttributesBasedUserProviderInterface
{
public function loadUserByIdentifier(string $identifier, array $attributes = []): UserInterface
{
// реализуйте вашу собственную логику, чтобы загрузить и вернуть объект пользователя
}
}
2) Сконфигрируйте OidcTokenHandler
OidcTokenHandler
требует пакеты web-token/jwt-signature
,
web-token/jwt-checker
и web-token/jwt-signature-algorithm-ecdsa
.
Если вы ещё их не установили, выполните эти команды:
1 2 3
$ composer require web-token/jwt-signature
$ composer require web-token/jwt-checker
$ composer require web-token/jwt-signature-algorithm-ecdsa
Symfony предоставляет общий OidcTokenHandler
для декодирования вашего токена, проверки
и получения из него информации о пользователе:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc:
# Algorithm used to sign the JWS
algorithm: 'ES256'
# A JSON-encoded JWK
key: '{"kty":"...","k":"..."}'
# Audience (`aud` claim): required for validation purpose
audience: 'api-example'
# Issuers (`iss` claim): required for validation purpose
issuers: ['https://oidc.example.com']
Согласно спецификации OpenID Connect, по умолчанию в качестве идентификатора пользователя
используется утверждение sub
. Чтобы использовать другое утверждение, укажите его в файле
конфигурации:
1 2 3 4 5 6 7 8 9 10 11 12
# config/packages/security.yaml
security:
firewalls:
main:
access_token:
token_handler:
oidc:
claim: email
algorithm: 'ES256'
key: '{"kty":"...","k":"..."}'
audience: 'api-example'
issuers: ['https://oidc.example.com']
По умолчанию, OidcTokenHandler
создаёт OidcUser
с утверждениями. Чтобы
создать собственного Пользователя на основе утверждений, вы должны
создать собственный UserProvider:
1 2 3 4 5 6 7 8 9 10
// src/Security/Core/User/OidcUserProvider.php
use Symfony\Component\Security\Core\User\AttributesBasedUserProviderInterface;
class OidcUserProvider implements AttributesBasedUserProviderInterface
{
public function loadUserByIdentifier(string $identifier, array $attributes = []): UserInterface
{
// реализуйте вашу собственную логику, чтобы загрузить и вернуть объект пользователя
}
}
Создание пользователей из токена
Некоторые типы токенов (например, OIDC) содержат всю информацию, необходимую
для создания сущности пользователя (например, имя пользователя и роли). В этом случае
вам не нужен провайдер пользователей для создания пользователя из базы данных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/Security/AccessTokenHandler.php
namespace App\Security;
// ...
class AccessTokenHandler implements AccessTokenHandlerInterface
{
// ...
public function getUserBadgeFrom(string $accessToken): UserBadge
{
// получить данные из токена
$payload = ...;
return new UserBadge(
$payload->getUserId(),
fn (string $userIdentifier) => new User($userIdentifier, $payload->getRoles())
);
}
}
При использовании этой стратегии вы можете опустить конфигурацию user_provider
.
для брандмауэров без состояния .