Рассмотрим типичный сценарий аутентификации и авторизации для 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.