Как использовать аутентификацию токенов доступа

Дата обновления перевода 2023-01-12

Как использовать аутентификацию токенов доступа

Токены доступа или токены API часто используются как механизм аутентификации в контексте API. Токен доступа - это строка, получення во время аутентификации (используя приложение или сервер авторизации). Роль токена доступа заключается в верификации личности пользователя и получения согласия до выпуска токена.

Токены доступа могут быть любого вида, например, непрозрачными строками, веб-токенами JSON (JWT) или SAML2 (XML-структурами). Пожалуйста, прочтите RFC6750: Фреймворк авторизации OAuth 2.0: Использование токена предъявителя, чтобы узнать детали.

Использование аутентификатора токена доступа

Это руководство предполагает, что вы настроили безопасность и создали объект пользователя в вашем приложении. Следуйте основному руководству по безопасности, если это еще не так.

1) Сконфигурируйте аутентификатор токена доступа

Чтобы использовать аутентификатор токена доступа, вы должны сконфигурировать token_handler. Обработчик токенов получает токен из запроса и возврращает правильный идентификатор пользователя. Чтобы получить идентификатор пользователя, реализации может быть нужно загрузить и валидировать токен (например, аннулирование, срок действия, цифровую подпись и т.д.).

  • YAML
  • XML
  • PHP
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.

  • YAML
  • XML
  • PHP
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'

Возможно установить несколько экстракторов. В таком случае, порядок имеет значение: первый в списке вызывается первым.

  • YAML
  • XML
  • PHP
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:

  • YAML
  • XML
  • PHP
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.