Построение архитектуры микросервисов возможно с минимальным количеством кода, если вы используете Spring Boot, Spring Cloud и Spring Cloud Config. Упакуйте все в контейнеры Docker, и вы сможете запускать все с помощью Docker Compose. Если вы обмениваетесь данными между службами, вы можете обеспечить некоторую безопасность своих служб, не раскрывая их порты в вашем файле docker-compose.yml
.
Но что произойдет, если кто-то случайно откроет порты ваших микросервисных приложений? Будут ли они по-прежнему в безопасности или кто-нибудь сможет получить доступ к их данным?
В этом посте я покажу вам, как использовать HTTPS и OAuth 2.0 для обеспечения безопасной связи между службами.
Разработайте стек микросервисов с помощью Spring Boot, Spring Cloud и Spring Cloud Config
Я собираюсь сократить процесс создания полного стека микросервисов с помощью Spring Boot, Spring Cloud и Spring Cloud Config. Мой приятель Рафаэль написал пост о том, как создавать микросервисы Spring и настраивать их для производства . Вы можете использовать его пример приложения в качестве отправной точки. Клонируйте okta-spring-microservices-docker-example проект:
git clone https://github.com/oktadeveloper/okta-spring-microservices-docker-example.git spring-microservices-security cd spring-microservices-security
Для этого проекта требуются два приложения OpenID Connect на Okta, одно для разработки и одно для производства. Вам нужно будет создать каждое приложение на Okta, если вы не проходили вышеупомянутый учебник.
Создавайте приложения OpenID Connect на Okta
Вы можете зарегистрироваться для бесплатной учетной записи разработчика , которая позволит вам иметь до 1000 активных пользователей в месяц за 0 долларов. Этого должно быть достаточно для данного примера.
Почему Окта? Потому что аутентификацию писать неинтересно. В Okta есть API аутентификации и управления пользователями, которые позволяют вам быстрее разрабатывать свои приложения. Наш API и SDK облегчают вам аутентификацию, управление и защиту ваших пользователей за считанные минуты.
После создания учетной записи создайте новое веб-приложение на панели управления Okta ( Приложения > Добавить приложение ). Дайте приложению имя, которое вы запомните, продублируйте существующий URI перенаправления входа в систему и заставьте его использовать HTTPS. Нажмите Сделано .
Результат должен выглядеть так, как показано на скриншоте ниже.
Создайте другое приложение для производства. Я вызвал свои Микросервисы Prod
.
В проекте, который вы клонировали, измените config/school-ui.properties
, чтобы получить настройки из вашего приложения разработчика.
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default okta.oauth2.clientId={devClientId} okta.oauth2.clientSecret={devClientId}
Эти настройки будут использоваться при индивидуальном запуске ваших приложений с помощью Maven. Производственные настройки используются при запуске с помощью Docker Compose. Измените config-data/school-ui-production.properties
, чтобы получить настройки из вашего производственного приложения.
okta.oauth2.clientId={prodClientId} okta.oauth2.clientSecret={prodClientId}
Вы можете видеть, что spring.profiles.active
включает рабочий профиль в docker-compose.yml
:
school-ui: image: developer.okta.com/microservice-docker-school-ui:0.0.1-SNAPSHOT environment: - JAVA_OPTS= -DEUREKA_SERVER=http://discovery:8761/eureka -Dspring.profiles.active=production restart: on-failure depends_on: - discovery - config ports: - 8080:8080
Docker Compose запускается из каталога, расположенного над приложениями, и считывает свои данные из каталога config-data
. По этой причине вам нужно будет скопировать эти файлы свойств в этот каталог. Выполните следующие команды из корня этого проекта.
cp config/*.properties config-data/.
Запустите свой стек микросервисов Spring с помощью Docker Compose
В этом проекте есть агрегатор pom.xml
в его корневом каталоге, что позволит вам создавать все проекты с помощью одной команды. Выполните следующие команды Maven для создания, тестирования и создания образов Docker для каждого проекта.
mvn clean install
СОВЕТ : Если у вас не установлен Maven, вы можете установить его с помощью SDKMAN! sdk установить maven
Когда процесс завершится, запустите все приложения { config, discovery, school-service и school-ui } с помощью Docker Compose. Смотрите Install Docker Compose если он у вас не установлен.
docker-compose up -d
СОВЕТ : Вы можете использовать Kitematic для просмотра журналов каждого приложения по мере его запуска.
Перейдите к http://localhost:8080
в вашем любимом браузере. После этого вы должны иметь возможность войти в систему и просмотреть список школьных классов.
Безопасность Spring и OAuth 2.0
В этом примере используется стартер весенней загрузки Okta , который представляет собой тонкий слой поверх Spring Security. Okta starter упрощает настройку и выполняет проверку аудитории в токене доступа. Это также позволяет вам указать утверждение, которое будет использоваться для создания Spring Security authorities.
Файл docker-compose.yml
не предоставляет доступ к school-service
внешнему миру. Он делает это, не указывая ports
.
В проекте school-ui
есть класс School Controller
, который взаимодействует с school-service
с помощью Spring/| RestTemplate .
@GetMapping("/classes") @PreAuthorize("hasAuthority('SCOPE_profile')") public ResponseEntity> listClasses() { return restTemplate .exchange("http://school-service/class", HttpMethod.GET, null, new ParameterizedTypeReference
>() {}); }
Вы заметите, что на конечной точке этого класса есть защита, но между службами безопасности не существует. Я покажу вам, как решить эту проблему в следующих шагах.
Во-первых, откройте порт school-service
, чтобы имитировать, как кто-то жирно перебирает конфигурацию. Измените конфигурацию school-service
в docker-compose.yml
, чтобы открыть его порт.
school-service: image: developer.okta.com/microservice-docker-school-service:0.0.1-SNAPSHOT environment: - JAVA_OPTS= -DEUREKA_SERVER=http://discovery:8761/eureka depends_on: - discovery - config ports: - 8081:8081
Перезапустите все с помощью Docker Compose:
docker-compose down docker-compose up -d
Вы увидите, что вам не нужно проходить аутентификацию, чтобы просмотреть данные по адресу http://localhost:8081
. Черт возьми! 😱
Убедитесь, что закрыл все ваши контейнеры Docker, прежде чем перейти к следующему разделу.
docker-compose down
HTTPS Везде!
HTTPS расшифровывается как “Безопасный” HTTP. HTTPS-соединения зашифрованы, и их содержимое значительно сложнее прочитать, чем HTTP-соединения. В последние годы наблюдается большое движение за повсеместное использование HTTPS, даже при разработке. Есть проблемы, с которыми вы можете столкнуться при работе с HTTPS, и хорошо бы выявить их на ранней стадии.
Let’s Encrypt – это центр сертификации, который предлагает бесплатные сертификаты HTTPS. У него также есть API-интерфейсы для автоматизации их обновления. Короче говоря, это делает HTTPS настолько простым, что нет причин не использовать его! См. Добавьте социальный логин в свое приложение JHipster/| для получения инструкций по использованию certbot с помощью Let's Encrypt для создания сертификатов.
Я также призываю вас проверить Пружинный загрузочный стартер ACME . Это загрузочный модуль Spring, который упрощает генерацию сертификатов с использованием Let’s Encrypt и протокола Automatic Certificate Management Environment (ACME).
Упростите локальный TLS с помощью mkcert
Недавно я нашел инструмент под названием mkcert , который позволяет создавать сертификаты localhost
. Вы можете установить его с помощью Homebrew на macOS:
brew install mkcert brew install nss # Needed for Firefox
Если вы используете Linux, вам нужно будет установить certutil
первый:
sudo apt install libnss3-tools
Затем запустите команду brew install mkcert
, используя Linuxbrew . Пользователи Windows могут использовать шоколад или совок .
Выполните следующие команды mkcert
для создания сертификата для localhost
, 127.0.0.1
, имя вашего компьютера и хост discovery
(как указано в docker-compose.yml
).
mkcert -install mkcert localhost 127.0.0.1 ::1 `hostname` discovery
Если при этом генерируются файлы с номером, переименуйте файлы так, чтобы у них не было номера.
mv localhost+2.pem localhost.pem mv localhost+2-key.pem localhost-key.pem
HTTPS с пружинной загрузкой
Spring Boot не поддерживает сертификаты с расширением PEM , но вы можете преобразовать его в расширение PKCS12
, которое поддерживает Spring Boot. Вы можете использовать OpenSSL для преобразования сертификата и закрытого ключа в PKCS12. Это также будет необходимо для шифрования сгенерированных сертификатов Let’s Encrypt.
Запустите openssl
для преобразования сертификата:
openssl pkcs12 -export -in localhost.pem -inkey \ localhost-key.pem -out keystore.p12 -name bootifulsecurity
Укажите пароль при появлении соответствующего запроса.
Создать файл https.env
в корне вашего проекта и укажите следующие свойства, чтобы включить HTTPS.
export SERVER_SSL_ENABLED=true export SERVER_SSL_KEY_STORE=../keystore.p12 export SERVER_SSL_KEY_STORE_PASSWORD={yourPassword} export SERVER_SSL_KEY_ALIAS=bootifulsecurity export SERVER_SSL_KEY_STORE_TYPE=PKCS12
Обновите файл .gitignore
, чтобы исключить .env
файлы таким образом, пароль хранилища ключей не попадает в систему управления версиями.
*.env
Запустите source https.en
, чтобы установить эти переменные среды. Или, что еще лучше, добавьте этот лайк в свой .bashrc
или .zshrc
файл, чтобы эти переменные устанавливались для каждой новой оболочки. Да, вы также можете включить их в application.properties
каждого приложения, но тогда вы храните секреты в системе управления версиями. Если вы не проверяете этот пример в системе управления версиями, вот настройки, которые вы можете скопировать/вставить.
server.ssl.enabled=true server.ssl.key-store=../keystore.p12 server.ssl.key-store-password: {yourPassword} server.ssl.key-store-type: PKCS12 server.ssl.key-alias: bootifulsecurity
Запустите приложение discovery
:
cd discovery source ../https.env mvn spring-boot:run
Затем подтвердите, что вы можете получить к нему доступ по адресу https://localhost:8761
.
Открыть docker-compose.yml
и измените все экземпляры http
на https
. Редактировать school-ui/src/main/java/.../ui/controller/SchoolController.java
чтобы изменить вызов на school-service
для использования HTTPS.
return restTemplate .exchange("https://school-service/class", HttpMethod.GET, null, new ParameterizedTypeReference>() {});
Обновите {config,school-service,school-ui}/src/main/resources/application.properties
, чтобы добавить свойства, которые заставляют каждый экземпляр регистрироваться как защищенное приложение .
eureka.instance.secure-port-enabled=true eureka.instance.secure-port=${server.port} eureka.instance.status-page-url=https://${eureka.hostname}:${server.port}/actuator/info eureka.instance.health-check-url=https://${eureka.hostname}:${server.port}/actuator/health eureka.instance.home-page-url=https://${eureka.hostname}${server.port}/
Кроме того, измените адрес Eureka в каждом application.properties
(и в bootstrap.yml
) на https://localhost:8761/eureka
.
ПРИМЕЧАНИЕ : В application.properties
в проекте school-ui
не указан порт. Вам нужно будет добавить server.port=8080
.
На этом этапе вы должны иметь возможность запускать все свои приложения, выполнив следующее в каждом проекте (в отдельных окнах терминала).
source ../https.env ./mvnw spring-boot:start
Подтвердите, что все это работает по адресу https://localhost:8080
. Затем убейте все с помощью killall java
.
Использование HTTPS с помощью Docker Compose
Docker не считывает данные из переменных среды, он не знает о вашем локальном центре сертификации (Центре сертификации), и вы не можете добавлять файлы из родительского каталога в изображение.
Чтобы исправить это, вам нужно скопировать хранилище ключей.p12
и localhost.pem
в каталог каждого проекта. Первый будет использоваться для загрузки Spring, а второй будет добавлен в хранилище ключей Java на каждом изображении.
cp localhost.pem keystore.p12 config/. cp localhost.pem keystore.p12 discovery/. cp localhost.pem keystore.p12 school-service/. cp localhost.pem keystore.p12 school-ui/.
Затем измените Dockerfile
каждого проекта, чтобы скопировать сертификат и добавить его в хранилище доверия.
FROM openjdk:8-jdk-alpine VOLUME /tmp ADD target/*.jar app.jar ADD keystore.p12 keystore.p12 USER root COPY localhost.pem $JAVA_HOME/jre/lib/security RUN \ cd $JAVA_HOME/jre/lib/security \ && keytool -keystore cacerts -storepass changeit -noprompt \ -trustcacerts -importcert -alias bootifulsecurity -file localhost.pem ENV JAVA_OPTS="" ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar"]
Затем создайте файл .env
с переменными среды для Spring Boot и HTTPS.
SERVER_SSL_ENABLED=true SERVER_SSL_KEY_STORE=keystore.p12 SERVER_SSL_KEY_STORE_PASSWORD={yourPassword} SERVER_SSL_KEY_ALIAS=bootifulsecurity SERVER_SSL_KEY_STORE_TYPE=PKCS12 EUREKA_INSTANCE_HOSTNAME={yourHostname}
Вы можете получить значение для {yourHostname}
, выполнив команду hostname
.
Docker Compose имеет параметр конфигурации “env_file”, который позволяет вам считывать этот файл для переменных среды. Обновление docker-compose.yml
для указания env_file
для каждого приложения.
version: '3' services: discovery: env_file: - .env ... config: env_file: - .env ... school-service: env_file: - .env ... school-ui: env_file: - .env ...
Вы можете убедиться, что он работает, запустив docker-compose config
из вашего корневого каталога.
Запустите mvn clean install
, чтобы перестроить все ваши образы Docker с включенным HTTPS для регистрации в Eureka. Тогда начинайте все сначала.
docker-compose up -d
Теперь все ваши приложения работают в Docker по протоколу HTTPS! Докажите это в https://localhost:8080
.
ПРИМЕЧАНИЕ : Если ваши приложения не запускаются или не могут взаимодействовать друг с другом, убедитесь, что ваше имя хоста соответствует тому, что у вас есть в .env
.
Вы можете сделать еще одно улучшение безопасности: используйте OAuth 2.0 для защиты вашего школьного API-интерфейса.
Безопасность API с помощью OAuth 2.0
Добавьте Okta Spring Boot Starter и конфигурацию Spring Cloud в school-service/pom.xml
:
com.okta.spring okta-spring-boot-starter 1.1.0 org.springframework.cloud spring-cloud-starter-config
Затем создайте SecurityConfiguration.java
класс в school-service/src/main/java/.../service/configuration
:
package com.okta.developer.docker_microservices.service.configuration; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests().anyRequest().authenticated() .and() .oauth2ResourceServer().jwt(); } }
Создайте файл school-service/src/test/resources/test.properties
и добавьте свойства, чтобы Oktas config прошел, и он не использовал обнаружение или сервер конфигурации при тестировании.
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default okta.oauth2.clientId=TEST spring.cloud.discovery.enabled=false spring.cloud.config.discovery.enabled=false spring.cloud.config.enabled=false
Затем измените ServiceApplicationTests.java
чтобы загрузить этот файл для проверки свойств:
import org.springframework.test.context.TestPropertySource; ... @TestPropertySource(locations="classpath:test.properties") public class ServiceApplicationTests { ... }
Добавьте файл school-service/src/main/resources/bootstrap.yml
, который позволяет этому экземпляру считывать свою конфигурацию из конфигурации Spring Cloud.
eureka: client: serviceUrl: defaultZone: ${EUREKA_SERVER:https://localhost:8761/eureka} spring: application: name: school-service cloud: config: discovery: enabled: true serviceId: CONFIGSERVER failFast: true
Затем скопируйте config/school-ui.properties
, чтобы получить эквивалент school-service
.
cp config/school-ui.properties config/school-service.properties
Для Docker Compose вам также потребуется создать config-data/school-service.properties
со следующими настройками:
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default okta.oauth2.clientId={prodClientId} okta.oauth2.clientSecret={prodClientId}
Вам также нужно будет изменить docker-compose.yml
таким образом, school-service
перезапускается при сбое.
school-service: ... restart: on-failure
СОВЕТ : Вы могли бы создать сервисное приложение на Okta, которое использует учетные данные клиента, но этот пост уже достаточно сложен. Дополнительную информацию об этом подходе см. в разделе Безопасная связь между серверами с помощью Spring Boot и OAuth 2.0 .
Последний шаг, который вам нужно будет сделать, это изменить Школьный контролер
(в проекте
school-ui ), чтобы добавить токен доступа OAuth 2.0 к запросу, который он отправляет на |/school-server
.
Добавьте маркер доступа к RestTemplate
package com.okta.developer.docker_microservices.ui.controller; import com.okta.developer.docker_microservices.ui.dto.TeachingClassDto; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.ModelAndView; import java.io.IOException; import java.util.List; @Controller @RequestMapping("/") public class SchoolController { private final OAuth2AuthorizedClientService authorizedClientService; private final RestTemplate restTemplate; public SchoolController(OAuth2AuthorizedClientService clientService, RestTemplate restTemplate) { (1) this.authorizedClientService = clientService; this.restTemplate = restTemplate; } @RequestMapping("") public ModelAndView index() { return new ModelAndView("index"); } @GetMapping("/classes") @PreAuthorize("hasAuthority('SCOPE_profile')") public ResponseEntity> listClasses( @AuthenticationPrincipal OAuth2AuthenticationToken authentication) { (2) OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient( authentication.getAuthorizedClientRegistrationId(), authentication.getName()); (3) OAuth2AccessToken accessToken = authorizedClient.getAccessToken(); (4) restTemplate.getInterceptors().add(getBearerTokenInterceptor(accessToken.getTokenValue())); (5) return restTemplate .exchange("https://school-service/class", HttpMethod.GET, null, new ParameterizedTypeReference
>() {}); } private ClientHttpRequestInterceptor getBearerTokenInterceptor(String accessToken) { return (request, bytes, execution) -> { request.getHeaders().add("Authorization", "Bearer " + accessToken); return execution.execute(request, bytes); }; } }
1 Добавить Авторизованная клиентская служба OAuth2
зависимость от конструктора 2 Ввести Токен аутентификации OAuth2
в list Classes()
метод 3 Создать
OAuth2AuthorizedClient из
аутентификации 4 Получите токен доступа от авторизованного клиента 5 Добавьте маркер доступа в заголовок
Authorization
Вот и все! Поскольку school-ui
и school-service
используют одни и те же настройки приложения OIDC, сервер распознает и проверяет токен доступа (который также является JWT) и разрешает доступ.
На этом этапе вы можете выбрать запуск всех ваших приложений по отдельности с помощью ./mvn spring-boot:run
или с помощью Docker Compose. Последний метод требует всего нескольких команд.
mvn clean install docker-compose down docker-compose up -d
Используйте HTTP Basic Auth для безопасной связи микросервиса с Eureka и Spring Cloud Config
Чтобы еще больше повысить безопасность между вашими микросервисами, сервером Eureka и конфигурацией Spring Cloud, вы можете добавить базовую аутентификацию HTTP. Чтобы сделать это, вам нужно будет добавить spring-boot-starter-security
в качестве зависимости как в config
и открытие
проектов. Затем вам нужно будет указать spring.security.user.password
для каждого из них и зашифровать его. Вы можете узнать больше о том, как это сделать, в Spring Cloud Config security docs .
Как только вы настроите Spring Security в обоих проектах, вы можете настроить URL-адреса так, чтобы в них были указаны имя пользователя и пароль. Например, вот как будет выглядеть настройка в school-ui
project/| bootstrap.yml :
eureka: client: serviceUrl: defaultZone: ${EUREKA_SERVER:https://username:password@localhost:8761/eureka}
Вам нужно будет внести аналогичные изменения в URL-адреса в docker-compose.yml
.
Расширьте свои знания о микросервисах Spring, Docker и OAuth 2.0
В этом руководстве показано, как обеспечить безопасность взаимодействия между службами в архитектуре микросервисов. Вы узнали, как использовать HTTPS везде и блокировать свой API с помощью OAuth 2.0 и JWTs.
Вы можете найти исходный код этого примера на GitHub по адресу oktadeveloper/okta-spring-microservices-https-example .
Если вы хотите немного подробнее изучить эти темы, я думаю, вам понравятся следующие записи в блоге:
Создавайте микросервисы Spring и настраивайте их для производства
Создайте архитектуру микросервисов для микро-пивоварен с помощью Spring Boot
Создавайте и защищайте микросервисы с помощью Spring Boot 2.0 и OAuth 2.0
Разработка архитектуры микросервисов с использованием OAuth 2.0 и JHipster
Безопасная связь между серверами с помощью Spring Boot и OAuth 2.0
Эти записи в блоге помогли заставить все работать в этом посте:
Есть вопросы? Задайте их в комментариях ниже! Если ваш вопрос не относится к этому сообщению, пожалуйста, разместите его на наших форумах разработчиков .
Чтобы получать уведомления о новых публикациях в нашем блоге, посвященных технологиям, следуйте за нами @oktadev в Twitter или подписывайтесь на наш канал YouTube .
Оригинал: “https://dev.to/oktadev/secure-service-to-service-spring-microservices-with-https-and-oauth-2-0-2889”