Пишем Java веб-приложение на современном стеке. С нуля до микросервисной архитектуры. Часть 3

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


2022-01-20 13:56

разработка по

В прошлых частях мы успешно спроектировали и запустили два микросервиса: сервис BookStore и сервис аутентификации/авторизации.

Теперь мы можем расположить каждый из них на отдельном инстансе (например в AWS EC2), но в таком случае они будут выглядеть не как одно целое для конечного потребителя. Далее при развитии архитектуры количество сервисов будет только увеличиваться, и нам понадобится что-то, что свяжет наши сервисы и будет маршрутизировать запросы пользователя на каждый из них. Для этих целей используют шаблон проектирования API Gateway, который позволяет реализовать единую точку входа в нашу систему, и перенаправляет запросы на нужный микросервис.

Существует множество реализаций Api Gateway — например, Kong, Gravitee, Krakend и т.д. Мы же воспользуемся решением для экосистемы Spring Cloud — Spring Cloud Gateway.

Spring Cloud Gateway представляет собой Spring-Boot приложение, которое позволяет настраивать маршрутизацию в yaml-конфиге или в коде приложения, а также расширять логику путём написания своего кода. Реализуем схему, при которой все запросы на эндпоинт /registration и /login будут перенаправляться на сервис авторизации, /help будет вести на сайт spring, а остальные запросы будут маршрутизироваться в BookStore. Запускать всё вместе будем на одном инстансе на разных портах (аналогично реализуется на нескольких инстансах).

Начнём с генерации gradle-проекта на start.spring.io. После добавления компонента Gateway должен получиться следующий build.gradle:

plugins { 	id 'org.springframework.boot' version '2.6.2' 	id 'io.spring.dependency-management' version '1.0.11.RELEASE' 	id 'java' }  group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11'  repositories { 	mavenCentral() }  ext { 	set('springCloudVersion', "2021.0.0") }  dependencies { 	implementation 'org.springframework.cloud:spring-cloud-starter-gateway' 	testImplementation 'org.springframework.boot:spring-boot-starter-test' }  dependencyManagement { 	imports { 		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 	} }  test { 	useJUnitPlatform() }

Код Main-класса:

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

На этом всё, остается сконфигурировать маршруты для наших запросов в соответствии со схемой. Как я говорил, конфигурацию можно писать кодом или в yaml-файле, который и будем использовать вместо application.properties:

server:   port: 80  spring:   cloud:     gateway:       httpclient:         ssl:           useInsecureTrustManager: true       routes:         - id: bookstore           uri: http://localhost:8080           predicates:             - Path=/books/**

В секции routes указываются правила для обработки входящий запросов. В данном случае правило предписывает все запросы на эндпоинт /books перенаправлять на http://localhost:8080. То есть Gateway, получив такой запрос, сделает вызов своим http-клиентом на http://localhost:8080 и полученный ответ вернёт пользователю.

Добавим теперь правила для сервиса авторизации, продолжив список в yaml-конфигурации (обращая внимание на табуляции, так как лишний tab может привести к ошибке, и приложение не запустится):

- id: registration   uri: http://localhost:8081   predicates:     - Path=/registration   filters:     - RewritePath=/registration, /auth  - id: token   uri: http://localhost:8081   predicates:     - Path=/login   filters:     - RewritePath=/login, /auth/token

Здесь у нас появились фильтры — сущности, позволяющие модифицировать запрос. Полное описание фильтров можно найти в документации. Как можно догадаться из названия, RewritePath модифицирует путь запроса. Приходящие запросы на эндпоинты /registration и /login будут перенаправлены на сервис авторизации на эндпоинты /auth и /auth/token соответственно.

Добавим ещё одно правило, которое будет возвращать 302 Redirect на страницу с руководствами Spring:

- id: help   uri: https://spring.io/guides   predicates:     - Path=/help   filters:     - RedirectTo=302, https://spring.io/guides

Отправив запрос на /help, пользователь будет получать 302, и браузер будет редиректить на страницу https://spring.io/guides.

Запустим все три приложения (на портах 8080, 8081 и 80) и попытаемся сделать запросы через Api Gateway.

Регистрация

curl --location --request POST 'http://localhost/registration'  --header 'Content-Type: application/json'  --data-raw '{     "clientId": "admin",     "clientSecret": "password" }'  HTTP/1.1 200 OK Content-Type: text/plain;charset=UTF-8 Date: Thu, 06 Jan 2022 10:15:00 GMT content-length: 10   Registered

Аутентификация

curl --location --request POST 'http://localhost/login'  --header 'Content-Type: application/json'  --data-raw '{     "clientId": "admin",     "clientSecret": "password" }'  HTTP/1.1 200 OK transfer-encoding: chunked Content-Type: application/json Date: Thu, 06 Jan 2022 10:16:20 GMT   { "token" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJib29rc3RvcmUiLCJzdWIiOiJhZG1pbiIsImlzcyI6ImF1dGgtc2VydmljZSIsImV4cCI6MTY0MTQ2NDQ4MCwiaWF0IjoxNjQxNDY0MTgwfQ.qTVmUmfG4tro_D9ui7iSrBGZBi0Z0ob653kKV2vjjp0" }

Получение данных

curl --location --request GET 'http://localhost/books'  --header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJib29rc3RvcmUiLCJzdWIiOiJhZG1pbiIsImlzcyI6ImF1dGgtc2VydmljZSIsImV4cCI6MTY0MTQ2NDQ4MCwiaWF0IjoxNjQxNDY0MTgwfQ.qTVmUmfG4tro_D9ui7iSrBGZBi0Z0ob653kKV2vjjp0'  HTTP/1.1 200 OK transfer-encoding: chunked Content-Type: application/json Date: Thu, 06 Jan 2022 10:17:58 GMT   [ { "id" : 1, "author" : "Joshua Bloch", "title" : "Effective Java", "price" : 54.99 }, { "id" : 2, "author" : "Kathy Sierra", "title" : "Head First Java", "price" : 12.66 }, { "id" : 3, "author" : "Benjamin J. Evans", "title" : "Java in a Nutshell: A Desktop Quick Reference", "price" : 28.14 } ]

Переадресация

curl --location --request GET 'http://localhost/help'  HTTP/1.1 302 Location: https://spring.io/guides content-length: 0

С помощью фильтров можно также добавлять необходимые заголовки в запросы, менять тело запроса и ответа, добавлять дополнительные параметры в запрос и так далее.

Отдельно хочется выделить фильтры CircuitBreaker и RequestRateLimiter, которые используются в высоконагруженных системах. RequestRateLimiter необходим для ограничания количества запросов, при превышении порога которых, пользователю будет возвращаться 429 Too Many Requests.

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

Входящие в состав Spring Cloud Gateway стандартные предикаты и фильтры в большинстве случаев покрывают все потребности. Однако, если вам понадобится реализовать что-то более сложное, то придется самостоятельно написать код. Особенностью фреймворка является его реактивность — он построен на основе Spring WebFlux и Project Reactor. Требуется некоторая подготовка и понимание принципов реактивного программирования.

Стоит отметить, что обычно Api Gateway также используется для терминирования https. То есть защищённое соединение устанавливается между пользователем и Gateway, а дальше трафик на внутренние сервисы идёт уже по протоколу http, что избавляет от необходимости работы с сертификатами на сотнях микросервисах.

Код проекта доступен на GitHub.


Источник: m.vk.com

Комментарии: