Введение
API заказов должен требовать входа для чтения и записи, оставляя проверку состояния общедоступной. Вы подключите проверку токенов к обоим маршрутам заказов, проверите принятые и отклонённые запросы и убедитесь, что отклонённые вызовы не изменяют бизнес-данные.
Сначала выполните Добавление входа в приложение с Cognito и Создание и проверка API заказов. Эта новая среда предоставляет работающий общедоступный бэкенд и синтетические данные идентификации; прежние ресурсы и токены не используются.
Связь с сертификацией
Эта лабораторная работа помогает на практике изучить следующие темы экзаменов.
- Solutions Architect – Associate (SAA-C03) · Задача 1.2: Авторизация API на основе токенов и проверка границ доступа.
- Developer – Associate (DVA-C02) · Задача 2.1: Авторизация API на основе токенов и проверка границ доступа.
- Security – Specialty (SCS-C03) · Задача 4.1: Базовая практика: Авторизация API на основе токенов и проверка границ доступа.
Проверка общедоступных маршрутов и вход
На этом шаге вы установите текущее состояние общедоступных маршрутов и получите настоящий токен доступа для нужного клиента приложения.
Перейдите в рабочий каталог и ограничьте доступ к создаваемым файлам токенов:
cd /home/labex/project
umask 077
Откройте AWS View рядом с Terminal, чтобы сравнивать запросы API, выполнения функций и сохранённые заказы. Журналы запросов шлюза и журналы выполнения Lambda раздельны: отклонённый токен должен остановить запрос до запуска функции.
Прочитайте безопасный перечень подготовленных ресурсов. В нём указаны нужный пул и клиент, другой клиент, другой издатель и несвязанный эталонный пул:
cat scenario.json
Выберите основные идентификаторы с помощью jq -r, сохраняя вывод через подстановку команды $(...):
POOL_ID=$(jq -r '.main.pool' scenario.json)
CLIENT_ID=$(jq -r '.main.client' scenario.json)
Найдите подготовленный HTTP API по имени и сохраните его сгенерированный идентификатор для очистки:
API_ID=$(aws apigatewayv2 get-apis \
--query "Items[?Name=='labex-a05'].ApiId | [0]" \
--output text)
printf '%s\n' "$API_ID" > api-id.txt
Проверьте типы авторизации маршрутов:
aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query 'Items[].{Route:RouteKey,Authorization:AuthorizationType}'
Изначально все три маршрута используют NONE. Сам по себе каталог пользователей ещё не защищает API. Составьте адрес API в рабочей среде:
API_URL="http://127.0.0.1:8081/api/$API_ID"
curl -i "$API_URL/health"
Ожидайте HTTP 200 и healthy: true, реальное выполнение проверки состояния в AWS View и отсутствие заказов.
Войдите под подготовленным синтетическим пользователем через нужный общедоступный клиент, сохранив настоящий ответ в приватном файле:
aws cognito-idp initiate-auth \
--client-id "$CLIENT_ID" \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters file://sign-in.json \
--query AuthenticationResult \
--output json > tokens.json
Извлеките токен доступа и токен ID, не выводя их:
jq -r '.AccessToken' tokens.json > access-token.txt
jq -r '.IdToken' tokens.json > id-token.txt
Используйте только токен доступа, чтобы прочитать актуальное имя пользователя приложения:
aws cognito-idp get-user \
--access-token "$(cat access-token.txt)" \
--query Username \
--output text
Ожидайте labex-demo. AWS View показывает подготовленного подтверждённого пользователя и клиент. Независимая проверка проверяет действительно выданный токен и актуальную идентификацию; она не выполняет повторный вход за вас.
Подключение авторизатора к обоим маршрутам заказов
На этом шаге вы настроите нужного издателя и аудиторию и примените один авторизатор JWT к защищённым чтению и записи.
Авторизатор JWT проверяет подписанный токен до того, как API Gateway вызовет защищённый маршрут. Издатель — подписанный идентификатор пула Cognito. Аудитория — нужный клиент приложения: API Gateway проверяет aud, если он присутствует, иначе client_id. Составьте адрес издателя из действительного идентификатора пула:
ISSUER="https://cognito-idp.us-east-1.amazonaws.com/$POOL_ID"
Используйте маркер here-document без кавычек, чтобы оболочка подставила оба идентификатора в обычный файл конфигурации:
cat > jwt-config.json <<JSON
{
"Issuer": "$ISSUER",
"Audience": ["$CLIENT_ID"]
}
JSON
Создайте авторизатор JWT. Одинарные кавычки сохраняют буквальный источник идентификации $request.header.Authorization, не позволяя оболочке раскрыть его как переменную:
AUTH_ID=$(aws apigatewayv2 create-authorizer \
--api-id "$API_ID" \
--name OrdersUsers \
--authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration file://jwt-config.json \
--query AuthorizerId \
--output text)
Найдите сгенерированный идентификатор каждого маршрута заказов:
READ_ROUTE_ID=$(aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query "Items[?RouteKey=='GET /orders'].RouteId | [0]" \
--output text)
WRITE_ROUTE_ID=$(aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query "Items[?RouteKey=='POST /orders'].RouteId | [0]" \
--output text)
API Gateway не всегда различает токены доступа и токены ID. Эти токены доступа, выданные самим Cognito при входе по паролю, содержат aws.cognito.signin.user.admin; требование этой области доступа отклоняет токен ID, в котором её нет. Область обозначает доступ пользователя Cognito к самообслуживанию, а не администрирование AWS или владение заказом. В этом упражнении не создаётся отдельная бизнес-область доступа.
Потребуйте JWT и эту область доступа на маршруте чтения:
aws apigatewayv2 update-route \
--api-id "$API_ID" \
--route-id "$READ_ROUTE_ID" \
--authorization-type JWT \
--authorizer-id "$AUTH_ID" \
--authorization-scopes aws.cognito.signin.user.admin \
--query '{Route:RouteKey,Authorization:AuthorizationType,Scopes:AuthorizationScopes}'
Примените то же требование к маршруту записи:
aws apigatewayv2 update-route \
--api-id "$API_ID" \
--route-id "$WRITE_ROUTE_ID" \
--authorization-type JWT \
--authorizer-id "$AUTH_ID" \
--authorization-scopes aws.cognito.signin.user.admin \
--query '{Route:RouteKey,Authorization:AuthorizationType,Scopes:AuthorizationScopes}'
Подготовленная стадия $default использует AutoDeploy, поэтому изменения маршрутов применяются автоматически. Снова прочитайте типы всех маршрутов:
aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query 'Items[].{Route:RouteKey,Authorization:AuthorizationType}'
Ожидайте JWT на обоих маршрутах заказов и NONE на GET /health. В AWS View сравните действительного издателя, нужную аудиторию, идентификаторы авторизатора маршрутов и требуемую область доступа. Создание авторизатора без подключения к каждому защищённому маршруту не защищает эти маршруты.

Пример: оба маршрута заказов используют нужный авторизатор и область доступа, а проверка состояния остаётся общедоступной. Сгенерированные идентификаторы ресурсов в вашей VM отличаются.

Проверка принятых и отклонённых запросов
На этом шаге вы докажете реальный доступ нужного пользователя к бизнес-операциям и отклонение запросов до Lambda при отсутствии или неподходящих токенах.
Токен bearer удостоверяет того, кто его предъявляет. Прочитайте его из приватного файла передачи в заголовок Authorization. Продолжайте использовать curl -i, чтобы показывать только заголовки и тело ответа; не включайте подробное журналирование заголовков запроса. Отправьте допустимый заказ с количеством 4:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat access-token.txt)" -H 'Content-Type: application/json' --data '{"id":"signed-order","quantity":4}'
Ожидайте HTTP 201 и сумму 1100 (4 × 250 + 100). Получите тот же сохранённый заказ с токеном доступа:
curl -i "$API_URL/orders?id=signed-order" -H "Authorization: Bearer $(cat access-token.txt)"
Ожидайте HTTP 200 с теми же идентификатором, количеством и рассчитанной суммой. Независимо проверьте запись непосредственно в сервисе:
aws dynamodb get-item \
--table-name labex-a05-orders \
--key '{"id":{"S":"signed-order"}}' \
--consistent-read \
--query Item
Теперь повторите чтение без токена:
curl -i "$API_URL/orders?id=signed-order"
Ожидайте HTTP 401 Unauthorized без приватной записи в ответе. Проверьте также анонимную запись:
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{"id":"reject-missing","quantity":9}'
Она тоже должна вернуть 401 и не создать запись. И защищённое чтение, и запись требуют авторизации.
В предоставленном образце с неверной подписью изменён один байт подписи. Ему нельзя доверять, даже если видимые данные утверждений выглядят правдоподобно:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat bad-signature-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-signature","quantity":9}'
Ожидайте 401. Действительно выданный токен другого клиента приложения также не подходит для этого API. Прочитайте безопасный идентификатор этого клиента, войдите через него и извлеките токен доступа в приватный файл:
WRONG_CLIENT_ID=$(jq -r '.wrong_client' scenario.json)
aws cognito-idp initiate-auth \
--client-id "$WRONG_CLIENT_ID" \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters file://sign-in.json \
--query AuthenticationResult \
--output json > wrong-client.json
jq -r '.AccessToken' wrong-client.json > wrong-client-token.txt
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat wrong-client-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-client","quantity":9}'
Ожидайте 401: одной действительной подписи недостаточно для соответствия настроенной аудитории. Затем используйте подготовленного другого издателя Cognito с собственным синтетическим пользователем и общедоступным клиентом:
OTHER_POOL_ID=$(jq -r '.other.pool' scenario.json)
OTHER_CLIENT_ID=$(jq -r '.other.client' scenario.json)
aws cognito-idp initiate-auth \
--client-id "$OTHER_CLIENT_ID" \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters file://sign-in.json \
--query AuthenticationResult \
--output json > wrong-issuer.json
jq -r '.AccessToken' wrong-issuer.json > wrong-issuer-token.txt
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat wrong-issuer-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-issuer","quantity":9}'
Ожидайте 401, поскольку подписанный издатель отличается от ожидаемого пула. Предоставленный просроченный образец подписан со значением exp, уже находящимся в прошлом; он проверяет временное правило без ожидания истечения действующего сеанса:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat expired-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-expired","quantity":9}'
Ожидайте 401. Токен ID подписан для этого клиента, но не содержит область доступа токена доступа, требуемую маршрутом:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat id-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-id-token","quantity":9}'
Ожидайте 403 из-за недостаточной области доступа. Подпись, издатель, аудитория и срок действия токена необходимы, но не предоставляют отсутствующих разрешений.
Убедитесь, что проверка состояния остаётся общедоступной и сохранена только допустимая бизнес-запись:
curl -i "$API_URL/health"
aws dynamodb scan --table-name labex-a05-orders --query Items
Ожидайте 200 для проверки состояния и ровно signed-order с суммой 1100. В AWS View сравните реальные API requests с CloudWatch Logs: отклонённая авторизация даёт результат 401/403 на шлюзе без соответствующего выполнения функции. Успешные защищённые запросы имеют реальные события и ответы Lambda, из которых исключены заголовки учётных данных. Основание оценки — эти результаты выполнения и API и данные самого сервиса, а не снимки экрана или локальные отметки об успехе.

Пример: допустимые запись и чтение заказа вернули 201/200; неподходящие токены вернули 401/403. Список выполнений функции и таблица сервиса независимо подтверждают, что отклонённые запросы не выполняли бизнес-запись.
Удаление защищённых маршрутов и ресурсов упражнения
На этом шаге вы удалите API, подготовленные данные идентификации и данные упражнения, сохранив только несвязанные эталоны.
В ваш перечень входят этот API и его авторизатор, функция и её группа журналов, группа журналов запросов шлюза, таблица заказов, политика роли OrdersData, основной пул (два клиента и один пользователь), пул другого издателя (один клиент и один пользователь) и приватные файлы входных данных и ответов. Эталонные пул, таблица, журналы и политика роли FunctionLogs не связаны с упражнением и должны остаться.
Удалите API вместе с его авторизатором, маршрутами, интеграцией и стадией:
aws apigatewayv2 delete-api --api-id "$API_ID"
aws lambda delete-function --function-name labex-a05-orders-api
aws logs delete-log-group --log-group-name /aws/lambda/labex-a05-orders-api
aws logs delete-log-group --log-group-name /aws/apigateway/labex-a05
aws dynamodb delete-table \
--table-name labex-a05-orders \
--query TableDescription.TableName \
--output text
aws iam delete-role-policy --role-name labex-a05-execution --policy-name OrdersData
Удалите синтетических пользователей и каждый клиент приложения упражнения, затем их пулы:
aws cognito-idp admin-delete-user --user-pool-id "$POOL_ID" --username labex-demo
aws cognito-idp delete-user-pool-client --user-pool-id "$POOL_ID" --client-id "$CLIENT_ID"
aws cognito-idp delete-user-pool-client \
--user-pool-id "$POOL_ID" \
--client-id "$WRONG_CLIENT_ID"
aws cognito-idp delete-user-pool --user-pool-id "$POOL_ID"
aws cognito-idp admin-delete-user --user-pool-id "$OTHER_POOL_ID" --username labex-demo
aws cognito-idp delete-user-pool-client \
--user-pool-id "$OTHER_POOL_ID" \
--client-id "$OTHER_CLIENT_ID"
aws cognito-idp delete-user-pool --user-pool-id "$OTHER_POOL_ID"
Успешно запросите перечни ресурсов, чтобы доказать отсутствие удалённых объектов; сетевые ошибки и ошибки аутентификации не подтверждают очистку:
aws apigatewayv2 get-apis --query 'Items[].Name'
aws lambda list-functions --query 'Functions[].FunctionName'
aws cognito-idp list-user-pools --max-results 60 --query 'UserPools[].Name'
aws dynamodb list-tables --query TableNames
Ожидайте пустые списки API и функций, только labex-a05-reference в списках пулов и таблиц и сохранённые эталонные данные и журналы в AWS View. Удалите только перечисленные приватные файлы:
rm -f tokens.json access-token.txt id-token.txt wrong-client.json wrong-client-token.txt wrong-issuer.json wrong-issuer-token.txt expired-token.txt future-iat-token.txt future-nbf-token.txt bad-signature-token.txt sign-in.json
Само по себе удаление локальных файлов не является универсальным отзывом JWT на сервере. Синтетические пользователи, клиенты, пулы и API упражнения также были удалены. Очистка эталонных ресурсов, окончательный отзыв учётных данных и выключение VM остаются задачами автора после этих проверок ресурсов только для чтения.
Резюме
Вы настроили авторизатор JWT Cognito и потребовали его для защищённых чтения и записи, сохранив общедоступную проверку состояния. Вы проверили реальные принятые запросы и отклонение отсутствующих, изменённых, выданных другому клиенту или издателю, просроченных токенов и токенов ID. Вы сопоставили реальные результаты шлюза с выполнениями Lambda и сохранёнными данными, затем удалили API, ресурсы идентификации и данные упражнения и приватные файлы, сохранив эталоны. Авторизация JWT не реализовала проверку владения отдельными заказами.



