Рассмотрим типичный сценарий аутентификации и авторизации для api
- Пользователь передает на сервер идентификатор / пароль как часть аутентификации.
- пароль проверяется, сервер генерирует токен и сохраняет токен в базе данных по идентификатору пользователя.
- Сервер отправляет токен клиенту.
- Клиент отправит токен обратно во всех последующих запросах API.
- Сервер проверит токен на соответствие токену, сохраненному в базе данных, и разрешит выполнение запроса при условии, что пользователь имеет правильную авторизацию в API.
В архитектуре микросервисов сервисы обмениваются данными друг с другом с помощью вызовов API. Здесь важно отметить, что каждая микрослужба независима и не использует общую базу данных. Как происходит авторизация для отдельного микросервиса?
Поскольку базы данных микросервисов не являются общими, сохранение токена в базе данных не поможет. Токен, сохраненный в базе данных одного микросервиса, недоступен для другого микросервиса. Здесь может помочь самопроверяемый токен (например, JWT). Первая служба получает токен с помощью аутентификации и может передать токен другой службе. Поскольку токен поддается самопроверке, вторая служба может проверить то же самое. Примером самопроверяемого токена является JWT.
JWT (веб-токен JSON) - https://jwt.io/
- В своем истинном определении JWT определяет компактный и автономный способ безопасной передачи информации между сторонами в виде объекта JSON.
- Этой информации можно доверять , потому что она имеет цифровую подпись.
- JWT могут быть подписаны с использованием секрета (с помощью алгоритма HMAC) или пары открытого / закрытого ключей с использованием RSA или ECDSA.
Проверка JWT
Важным моментом является то, что JWT является автономным и имеет цифровую подпись центрального сервера аутентификации. Подпись JWT может быть проверена, что может подтвердить, что этот JWT действительно сгенерирован Центральным сервером аутентификации.
Во время связи с микросервисами JWT может быть передан в запросе, а в точке входа api можно проверить JWT и установить контроль доступа для пользователя.
Связь с микросервисами (использование токена JWT)
Ниже показано, как может происходить связь между микросервисами.
- Первый api при аутентификации отправит идентификатор пользователя / пароль на центральный сервер аутентификации.
- Сервер аутентификации проверит пароль и сгенерирует JWT. Здесь следует отметить, что JWT не нужно сохранять в базе данных (в конце концов, он самодостаточен и поддается проверке)
- Клиент (первый api в цепочке) получает JWT.
- Первый api передаст токен JWT в заголовке запроса на второй api
- Второй api проверит JWT. JWT также содержит полезную нагрузку. В полезной нагрузке могут быть утверждения, а также дополнительная информация, например роль. Эта информация может использоваться, чтобы проверить, есть ли у пользователя авторизация на API.
Проверка JWT
- Обычно api в точке входа выполняет проверку JWT.
- С API-шлюзом (экспресс) эта работа будет перенесена на API-шлюз.
Экспресс-проверка JWT шлюза
- До сих пор мы говорили об общем определении JWT. Теперь мы поговорим о том, как настроить экспресс-шлюз для проверки JWT. Обратите внимание, что цель здесь не в том, чтобы показать непрерывный автоматизированный поток, а только для проверки JWT.
В предыдущей части (часть 1) мы говорили о двух услугах, первая - об одном клиенте, а вторая - о продукте. В этой статье мы продемонстрируем API защиты продукта с помощью JWT.
Шаг 1. Настройка пользователей для экспресс-шлюза
- Перейдите в путь, где установлен экспресс-шлюз
npm start [email protected] start /home/tanmay/express-gateway/my-gateway > node server.js gateway http server listening on :::8080 admin http server listening on 127.0.0.1:9876
Экспресс-шлюз запущен. Создайте одного пользователя (kevin) с помощью команды cli
tanmay@tanmay-VPCEB44EN:~/express-gateway$ eg users create ? Enter firstname [required]: kevin ? Enter lastname [required]: systrom ? Enter username [required]: kevin ? Enter email: [email protected] ? Enter redirectUri: http://localhost:4000/api/products Created 523c71a2-9f80-434f-a555-9b193ba66444 { "firstname": "kevin", "lastname": "systrom", "username": "kevin", "email": "[email protected]", "redirectUri": "http://localhost:4000/api/products", "isActive": true, "id": "523c71a2-9f80-434f-a555-9b193ba66444", "createdAt": "Thu Oct 11 2018 23:19:16 GMT+0530 (IST)", "updatedAt": "Thu Oct 11 2018 23:19:16 GMT+0530 (IST)"
Шаг 2. Создание учетных данных для JWT
tanmay@tanmay-VPCEB44EN:~/express-gateway$ eg credentials create -c kevin -t jwt ✔ Created 7dSTbOnvJ7mUF3CtNBCEst { "isActive": true, "createdAt": "Thu Oct 11 2018 23:22:40 GMT+0530 (IST)", "updatedAt": "Thu Oct 11 2018 23:22:40 GMT+0530 (IST)", "keyId": "7dSTbOnvJ7mUF3CtNBCEst", "keySecret": "7Ex0letChBSw23RfcPSqGr", "scopes": null, "consumerId": "523c71a2-9f80-434f-a555-9b193ba66444", "id": "7dSTbOnvJ7mUF3CtNBCEst" }
KeySecret будет использоваться для подписи JWT, а KeyId также будет использоваться в качестве входных данных для генерации JWT.
Шаг 3. Создание токена JWT
Здесь для иллюстрации рассмотрим программный подход к созданию JWT. В качестве альтернативы мы можем использовать онлайн-генерацию JWT на https://jwt.io/. Вот код того же.
let jwt = require('jsonwebtoken'); let secret = null; secret = '7Ex0letChBSw23RfcPSqGr' ; // this is the keySecret generated by eg credential create let token = jwt.sign( { "sub": "7dSTbOnvJ7mUF3CtNBCEst", //keyId generated by eg create "name": "Kevin Systrom", "iat": 1538828706 },secret); console.log(token); //verifying JWT token. let decoded = jwt.verify(token ,secToken); //Decoding JWT using keySecret console.dir(decoded, { depth : null ,colors : true});
Вывод
tanmay@tanmay-VPCEB44EN:~/json-web-token$ node json-web-token.js JWT : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3ZFNUYk9udko3bVVGM0N0TkJDRXN0IiwibmFtZSI6IktldmluIFN5c3Ryb20iLCJpYXQiOjE1Mzg4Mjg3MDZ9.gByvBq67l97PJ3Li_AeBeajKLXdxC8ILCth3aERKEHo decoded JWT payload: { sub: '7dSTbOnvJ7mUF3CtNBCEst', name: 'Kevin Systrom', iat: 1538828706 }
Шаг 4. Защитите маршрут API с помощью экспресс-шлюза.
Теперь, когда JWT сгенерирован, давайте настроим конфигурацию экспресс-шлюза для защиты API продукта. gateway.config.json имеет заполнитель для сохранения секретности декодирования JWT.
JWT - это политика, определяемая экспресс-шлюзом, как и прокси. Обратите внимание, что политика JWT привязана только к конвейеру default-2. а конвейер по умолчанию-2 определен для / api / product api (продукт apiEndPoint).
Ниже приведены важные параметры.
"jwt": [ { "action": { "secretOrPublicKey": "7Ex0letChBSw23RfcPSqGr", "checkCredentialExistence" : "false" } } ]
secretOrPublicKey - это ключ, который мы сгенерировали, например, с помощью команды create credential. Этот секрет будет использоваться для декодирования токена JWT. Мы вернемся к свойству checkCredentialExistence чуть позже. Полный gateway.config.json выглядит следующим образом.
{ "http": { "port": 8080 }, "admin": { "port": 9876, "hostname": "localhost" }, "apiEndpoints": { "api": { "host": "localhost", "paths": "/ip" }, "cust": { "host": "localhost", "paths": "/api/customers" }, "product": { "host": "localhost", "paths": "/api/products" } }, "serviceEndpoints": { "httpbin": { "url": "https://httpbin.org" }, "custsrv": { "url": "http://localhost:3000/" }, "prodsrv": { "url": "http://localhost:4000/" } }, "policies": [ "basic-auth", "key-auth", "cors", "expression", "log", "oauth2", "proxy", "rate-limit", "jwt" ], "pipelines": [ { "name": "default", "apiEndpoints": [ "api" ], "policies": [ { "proxy": [ { "action": { "serviceEndpoint": "httpbin", "changeOrigin": true } } ] } ] }, { "name": "default-1", "apiEndpoints": [ "cust" ], "policies": [ { "proxy": [ { "action": { "serviceEndpoint": "custsrv" } } ] } ] }, { "name": "default-2", "apiEndpoints": [ "product" ], "policies": [ { "jwt": [ { "action": { "secretOrPublicKey": "2URIWeWGe5srtOptNidOyP", "checkCredentialExistence" : "false" } } ] }, { "proxy": [ { "action": { "serviceEndpoint": "prodsrv" } } ] } ] } ] }
Шаг 5. Доступ к API продукта (без JWT)
Наступает момент истины. Мы попытаемся получить доступ к защищенному API (продукту) без передачи токена JWT. Запущу почтальон для тестирования api
Как и ожидалось, без передачи JWT мы получаем ошибку от api getway (unauthorized). Мы получаем доступ к / api / products, не передавая токены JWT gi, и шлюз api выдаст ошибку: unauthorized.
Шаг 6. Доступ к API продукта (с помощью JWT)
Передайте JWT в заголовке запроса следующим образом. Синтаксис был бы
Авторизация: Знаменосец eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3ZFNUYk9udko3bVVGM0N0TkJDRXN0IiwibmFtZSI6IktldmluIFN5c3Ryb20iLCJpYXQiOjE1Mzg4Mjg3MDZ9.gByvBq67l97PJ3Li_AeBeajKLXdxC8ILCth3aERKEHo
На этот раз вы получите успешный ответ от API, поскольку JWT будет декодирован шлюзом API.
Несколько замечаний
- По умолчанию все пользователи и учетные данные сохраняются в памяти. Рекомендуется использовать в памяти для dev и redis db для производственного сценария.
- Если мы отключим шлюз, все пользователи и их учетные данные будут потеряны.
- Для настройки Redis обратитесь к system.config.yml в папке config. Здесь параметр emulate четко указывает, что Redis работает в режиме «в памяти».
db: redis: emulate: true namespace: EG
- когда шлюз перезапущен, а пользователи и учетные данные потеряны, шлюз по-прежнему будет разрешать доступ к API продуктов. Это было бы из-за параметра ниже. Здесь в этом случае выполняется только проверка подписи, однако проверка наличия учетных данных не выполняется.
checkCredentialExistence : true
Резюме
- Шлюз API, как и Express, может упростить жизнь для общения по API и авторизации по API.
- Они могут защитить apis / ресурсы с помощью различных механизмов доступа (например, key-auth, oauth2, jwt)
- Сегодня мы видели краткий пример защиты ресурсов с помощью JWT.