Рубрики
Без рубрики

Обеспечьте безопасность в Zuul с помощью OAuth2 и JWT

Узнайте о том, как вы можете использовать пограничную службу Zuul в сочетании с OAuth и Jwt для защиты ваших веб-служб

Автор оригинала: baeldung.

1. введение

Проще говоря, архитектура микросервиса позволяет нам разбить нашу систему и наш API на набор автономных сервисов, которые могут быть развернуты полностью независимо.

Хотя это здорово с точки зрения непрерывного развертывания и управления, это может быстро усложниться, когда дело доходит до удобства использования API. С различными конечными точками для управления зависимым приложениям потребуется управлять CORS (Совместное использование ресурсов разных источников) и различным набором конечных точек.

Zuul-это пограничный сервис, который позволяет нам направлять входящие HTTP-запросы в несколько внутренних микросервисов. Во-первых, это важно для обеспечения единого API для потребителей наших внутренних ресурсов.

В принципе, Zuul позволяет нам объединить все наши сервисы, сидя перед ними и действуя в качестве прокси-сервера. Он получает все запросы и направляет их в нужную службу. Для внешнего приложения наш API отображается как единая область поверхности API.

В этом уроке мы поговорим о том, как мы можем использовать его именно для этой цели, в сочетании с OAuth 2.0 и JWTs , чтобы быть на переднем крае защиты наших веб-сервисов. В частности, мы будем использовать поток Предоставление пароля для получения маркера доступа к защищенным ресурсам.

Краткое, но важное замечание состоит в том, что мы используем поток предоставления паролей только для изучения простого сценария; большинство клиентов, скорее всего, будут использовать поток предоставления авторизации в производственных сценариях.

2. Добавление зависимостей Zuul Maven

Давайте начнем с добавления Zuul в наш проект . Мы делаем это, добавляя spring-cloud-starter-netflix-zuul артефакт:


    org.springframework.cloud
    spring-cloud-starter-netflix-zuul
    2.0.2.RELEASE

3. Включение Zuul

Приложение, которое мы хотели бы направить через Zuul, содержит сервер авторизации OAuth 2.0, который предоставляет токены доступа, и сервер ресурсов, который их принимает. Эти службы работают на двух отдельных конечных точках.

Мы хотели бы иметь единую конечную точку для всех внешних клиентов этих служб, с разными путями, ответвляющимися от разных физических конечных точек. Для этого мы представим Zuul в качестве пограничного сервиса.

Для этого мы создадим новое приложение Spring Boot под названием Приложение шлюза . Затем мы просто украсим этот класс приложения аннотацией @EnableZuulProxy , которая приведет к созданию экземпляра Zuul:

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
	SpringApplication.run(GatewayApplication.class, args);
    }
}

4. Настройка маршрутов Zuul

Прежде чем мы сможем пойти дальше, нам нужно настроить несколько свойств Zuul. Первое, что мы настроим, – это порт, на котором Zuul прослушивает входящие соединения. Это нужно ввести в файл /src/main/ресурсы/application.yml :

server:
    port: 8080

Теперь о забавных вещах, настройке реальных маршрутов, на которые будет перенаправляться Зуул. Для этого нам нужно отметить следующие службы, их пути и порты, которые они прослушивают.

Сервер авторизации развернут на: http://localhost:8081/spring-security-oauth-server/oauth

Сервер ресурсов развернут на: http://localhost:8082/spring-security-oauth-resource

Сервер авторизации является поставщиком удостоверений OAuth. Он существует для предоставления маркеров авторизации серверу ресурсов, который, в свою очередь, предоставляет некоторые защищенные конечные точки.

Сервер авторизации предоставляет Клиенту Маркер доступа, который затем использует этот маркер для выполнения запросов к Серверу ресурсов от имени Владельца Ресурса. Быстрый просмотр терминологии OAuth поможет нам сохранить эти концепции в поле зрения.

Теперь давайте наметим несколько маршрутов к каждой из этих служб:

zuul:
  routes:
    spring-security-oauth-resource:
      path: /spring-security-oauth-resource/**
      url: http://localhost:8082/spring-security-oauth-resource
    oauth:
      path: /oauth/**
      url: http://localhost:8081/spring-security-oauth-server/oauth	 

На этом этапе любой запрос, поступающий в Zuul на localhost:8080/oauth/** , будет перенаправлен в службу авторизации, работающую на порту 8081. Любой запрос на localhost:8080/spring-security-oauth-ресурс/** будет перенаправлен на сервер ресурсов, работающий на 8082.

5. Обеспечение Безопасности Внешних Транспортных Путей Zuul

Несмотря на то, что наша пограничная служба Zuul теперь правильно маршрутизирует запросы, она делает это без каких-либо проверок авторизации. Сервер авторизации , расположенный за /oauth/* , создает JWT для каждой успешной аутентификации. Естественно, он доступен анонимно.

Сервер ресурсов , расположенный по адресу /spring – security-oauth-resource/** , с другой стороны, всегда должен быть доступен с помощью JWT, чтобы гарантировать, что авторизованный клиент получает доступ к защищенным ресурсам.

Во-первых, мы настроим Zuul для прохождения через JWT к службам, которые находятся за ним. В нашем случае здесь эти службы сами должны проверить токен.

Мы делаем это, добавляя чувствительные заголовки: Cookie,Set-Cookie .

Это завершает нашу конфигурацию Zuul:

server:
  port: 8080
zuul:
  sensitiveHeaders: Cookie,Set-Cookie
  routes:
    spring-security-oauth-resource:
      path: /spring-security-oauth-resource/**
      url: http://localhost:8082/spring-security-oauth-resource
    oauth:
      path: /oauth/**
      url: http://localhost:8081/spring-security-oauth-server/oauth

После того, как мы покончим с этим, нам нужно будет разобраться с авторизацией на грани. Прямо сейчас Zuul не будет проверять JWT перед передачей его нашим нижестоящим службам. Эти службы сами проверят JWT, но в идеале мы хотели бы, чтобы пограничная служба сделала это первой и отклоняла любые несанкционированные запросы, прежде чем они распространятся глубже в нашу архитектуру.

Давайте настроим безопасность Spring, чтобы убедиться, что авторизация проверена в Zuul.

Во-первых, нам нужно будет включить в наш проект зависимости безопасности Spring. Мы хотим spring-security-oauth2 и spring-security-jwt:


    org.springframework.security.oauth
    spring-security-oauth2
    2.3.3.RELEASE


    org.springframework.security
    spring-security-jwt
    1.0.9.RELEASE

Теперь давайте напишем конфигурацию для маршрутов, которые мы хотим защитить, расширив ResourceServerConfigurerAdapter:

@Configuration
@Configuration
@EnableResourceServer
public class GatewayConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(final HttpSecurity http) throws Exception {
	http.authorizeRequests()
          .antMatchers("/oauth/**")
          .permitAll()
          .antMatchers("/**")
	  .authenticated();
    }
}

Класс Конфигурация шлюза определяет, как Spring Security должен обрабатывать входящие HTTP-запросы через Zuul. Внутри метода configure мы сначала сопоставили наиболее ограничительный путь с помощью antMatchers , а затем разрешили анонимный доступ через PermitAll .

То есть все запросы, поступающие в /oauth/** , должны быть разрешены без проверки каких-либо маркеров авторизации. Это имеет смысл, потому что это путь, по которому генерируются токены авторизации.

Затем мы сопоставили все остальные пути с/* * и посредством вызова аутентифицированного настояли на том, чтобы все остальные вызовы содержали маркеры доступа.

6. Настройка ключа, используемого для проверки JWT

Теперь, когда конфигурация установлена, все запросы, направленные по пути /oauth/** , будут разрешены анонимно, в то время как все остальные запросы потребуют аутентификации.

Однако есть одна вещь, которую мы здесь упускаем, и это фактический секрет, необходимый для проверки того, что JWT действителен. Для этого нам нужно предоставить ключ (в данном случае симметричный), используемый для подписи JWT. Вместо того, чтобы писать код конфигурации вручную, мы можем использовать spring-security-oauth2-автоматическая настройка .

Давайте начнем с добавления артефакта в наш проект:


    org.springframework.security.oauth.boot
    spring-security-oauth2-autoconfigure
    2.1.2.RELEASE

Затем нам нужно добавить несколько строк конфигурации в наше приложение .yaml файл для определения ключа, используемого для подписи JWT:

security:
  oauth2:
    resource:
      jwt:
        key-value: 123

Строка ключ-значение: 123 задает симметричный ключ, используемый Сервером авторизации для подписи JWT. Этот ключ будет использоваться spring-security-oauth2-автоматическая настройка для настройки синтаксического анализа токенов.

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

7. Тестирование пограничной службы

7.1. Получение токена доступа

Теперь давайте проверим, как ведет себя наш пограничный сервис Zuul – с помощью нескольких команд curl.

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

Здесь мы обмениваем имя пользователя и пароль на токен доступа . В этом случае мы используем ‘ john ‘в качестве имени пользователя и’ 123 ‘ в качестве пароля:

curl -X POST \
  http://localhost:8080/oauth/token \
  -H 'Authorization: Basic Zm9vQ2xpZW50SWRQYXNzd29yZDpzZWNyZXQ=' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=password&password=123&username=john'

Этот вызов дает токен JWT, который мы затем можем использовать для аутентифицированных запросов к нашему серверу ресурсов.

Обратите внимание на поле заголовка “Авторизация: Базовая…”|/. Это необходимо для того, чтобы сообщить серверу авторизации, какой клиент к нему подключается.

Это для клиента (в данном случае запрос cURL), что такое имя пользователя и пароль для пользователя:

{    
    "access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpX...",
    "token_type":"bearer",    
    "refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpX...",
    "expires_in":3599,
    "scope":"foo read write",
    "organization":"johnwKfc",
    "jti":"8e2c56d3-3e2e-4140-b120-832783b7374b"
}

7.2. Тестирование запроса сервера ресурсов

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

curl -X GET \
curl -X GET \
  http:/localhost:8080/spring-security-oauth-resource/users/extra \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Accept-Encoding: gzip, deflate' \
  -H 'Accept-Language: en-US,en;q=0.9' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV...' \
  -H 'Cache-Control: no-cache' \

Пограничная служба Zuul теперь будет проверять JWT перед маршрутизацией на сервер ресурсов.

Затем это извлекает ключевые поля из JWT и проверяет наличие более детальной авторизации, прежде чем отвечать на запрос:

{
    "user_name":"john",
    "scope":["foo","read","write"],
    "organization":"johnwKfc",
    "exp":1544584758,
    "authorities":["ROLE_USER"],
    "jti":"8e2c56d3-3e2e-4140-b120-832783b7374b",
    "client_id":"fooClientIdPassword"
}

8. Безопасность На Разных Уровнях

Важно отметить, что JWT проверяется пограничной службой Zuul перед передачей на сервер ресурсов. Если JWT недействителен, то запрос будет отклонен на границе пограничной службы.

С другой стороны, если JWT действительно действителен, запрос передается по потоку. Затем сервер ресурсов снова проверяет JWT и извлекает ключевые поля, такие как область действия пользователя, организация (в данном случае пользовательское поле) и полномочия. Он использует эти поля, чтобы решить, что пользователь может и не может делать.

Чтобы быть ясным, во многих архитектурах нам на самом деле не нужно будет дважды проверять JWT – это решение, которое вам придется принять на основе ваших моделей трафика.

Например, в некоторых производственных проектах доступ к отдельным серверам ресурсов может осуществляться как напрямую, так и через прокси – сервер, и мы можем захотеть проверить токен в обоих местах. В других проектах трафик может поступать только через прокси-сервер, и в этом случае проверки токена достаточно.

9. Краткое изложение

Как мы видели, Zuul предоставляет простой, настраиваемый способ абстрагирования и определения маршрутов для служб. Вместе с Spring Security это позволяет нам авторизовывать запросы на границах службы.

Наконец, как всегда, код доступен на Github .