javascript
使用HTTPS和OAuth 2.0保护服务到服务的Spring微服务
“我喜歡編寫身份驗證和授權代碼?!??從來沒有Java開發人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進行托管身份驗證,授權和多因素身份驗證。
如果您使用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構建完整的微服務堆棧的過程。 我的好友拉斐爾(Raphael)寫了一篇文章,介紹如何構建Spring微服務并將其Docker化用于生產 。 您可以使用他的示例應用程序作為起點。 克隆okta-spring-microservices-docker-example項目:
git clone https://github.com/oktadeveloper/okta-spring-microservices-docker-example.git spring-microservices-security cd spring-microservices-security此項目在Okta上需要兩個OpenID Connect應用程序,一個用于開發,一個用于生產。 如果沒有完成上述教程,則需要在Okta上創建每個應用程序。
在Okta上創建OpenID Connect應用程序
您可以注冊一個免費的開發人員帳戶 ,該帳戶最多可以有0個月活躍用戶,費用為$ 0。 對于這個例子來說應該足夠了。
為什么選擇Okta? 因為編寫身份驗證很無聊。 Okta具有身份驗證和用戶管理API,可讓您更快地開發應用。 我們的API和SDK使您可以在幾分鐘內輕松地進行身份驗證,管理和保護用戶安全。
創建帳戶后,在Okta的信息中心(“ 應用程序” >“ 添加應用程序” )中創建一個新的Web應用程序 。 給應用程序起一個您將記住的名稱,復制現有的登錄重定向URI,并使其使用HTTPS。 單擊完成 。
結果應類似于下面的屏幕截圖。
創建另一個用于生產的應用程序。 我給我的Prod Microservices 。
在您克隆的項目中,修改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看到spring.profiles.active打開生產配置文件:
school-ui:image: developer.okta.com/microservice-docker-school-ui:0.0.1-SNAPSHOTenvironment:- JAVA_OPTS=-DEUREKA_SERVER=http://discovery:8761/eureka-Dspring.profiles.active=productionrestart: on-failuredepends_on:- discovery- configports:- 8080:8080Docker Compose從應用程序上方的目錄中運行,并從config-data目錄讀取其數據。 因此,您需要將這些屬性文件復制到此目錄中。 從該項目的根目錄運行以下命令。
cp config/*.properties config-data/.使用Docker Compose啟動Spring微服務堆棧
該項目在其根目錄中有一個聚合器pom.xml ,使您可以使用一個命令來構建所有項目。 運行以下Maven命令為每個項目構建,測試和構建Docker映像。
mvn clean install如果您尚未安裝Maven,則可以使用SDKMAN進行安裝! sdk install maven
該過程完成后,使用Docker Compose啟動所有應用程序{config,discovery,school-service和school-ui}。 如果尚未安裝,請參閱安裝Docker Compose 。
docker-compose up -d您可以使用Kitematic查看每個應用程序啟動時的日志。
在您喜歡的瀏覽器中導航到http://localhost:8080 。 完成后,您應該可以登錄并查看學校課程列表。
Spring Security和OAuth 2.0
此示例使用Okta的Spring Boot Starter ,它是Spring Security之上的薄層。 Okta入門程序簡化了配置,并在訪問令牌中進行了觀眾驗證。 它還允許您指定將用于創建Spring Security授權的聲明。
docker-compose.yml文件不會將school-service公開給外界。 它通過不指定ports 。
school-ui項目有一個SchoolController類,該類使用Spring的RestTemplate與school-service進行RestTemplate 。
@GetMapping("/classes") @PreAuthorize("hasAuthority('SCOPE_profile')") public ResponseEntity<List<TeachingClassDto>> listClasses() {return restTemplate.exchange("http://school-service/class", HttpMethod.GET, null,new ParameterizedTypeReference<List<TeachingClassDto>>() {}); }您會注意到此類的端點上存在安全性,但是服務之間不存在安全性。 我將在下面的步驟中向您展示如何解決該問題。
首先,公開school-service的端口,以模擬有人用粗俗的方式進行配置。 在docker-compose.yml更改school-service配置以公開其端口。
school-service:image: developer.okta.com/microservice-docker-school-service:0.0.1-SNAPSHOTenvironment:- JAVA_OPTS=-DEUREKA_SERVER=http://discovery:8761/eurekadepends_on:- discovery- configports:- 8081:8081使用Docker Compose重新啟動一切:
docker-compose down docker-compose up -d您會看到不需要身份驗證即可查看http://localhost:8081 。 kes! 😱
在繼續下一部分之前, 請確保關閉所有Docker容器。
docker-compose downHTTPS無處不在!
HTTPS代表“安全” HTTP。 HTTPS連接經過加密,其內容比HTTP連接難讀得多。 近年來,即使在開發過程中,在所有地方都使用HTTPS的趨勢已經發生了很大的變化。 使用HTTPS時可能會遇到一些問題,因此盡早發現它們是很好的。
讓我們加密是一個提供免費HTTPS證書的證書頒發機構。 它還具有用于自動續訂的API。 簡而言之,它使HTTPS變得如此簡單,沒有理由不使用它! 有關如何將certbot與“讓我們加密”一起使用以生成證書的說明,請參閱將社交登錄名添加到您的JHipster應用程序 。
我也鼓勵您簽出Spring Boot Starter ACME 。 這是一個Spring Boot模塊,它使用Let's Encrypt和自動證書管理環境(ACME)協議簡化了生成證書的過程。
使用mkcert簡化本地TLS
我最近發現了一個名為mkcert的工具,該工具可以創建localhost證書。 您可以在macOS上使用Homebrew安裝它
brew install mkcert brew install nss # Needed for Firefox如果您使用的是Linux,則需要先安裝certutil :
sudo apt install libnss3-tools然后使用Linuxbrew運行brew install mkcert命令。 Windows用戶可以使用Chocolately或Scoop 。
執行以下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使用Spring Boot的HTTPS
Spring Boot不支持帶有PEM擴展名的證書,但是您可以將其轉換為Spring Boot支持的PKCS12擴展名。 您可以使用OpenSSL將證書和私鑰轉換為PKCS12。 這對于“加密我們生成的證書”也是必要的。
運行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.env來設置這些環境變量。 或者,甚至更好的方法是,將其添加到.bashrc或.zshrc文件中,以便為每個新Shell設置這些變量。 是的,您也可以將它們包含在每個應用程序的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<List<TeachingClassDto>>() {});更新{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}/另外,將每個application.properties (和bootstrap.yml )的Eureka地址更改為https://localhost:8761/eureka 。
school-ui項目中的application.properties沒有指定端口。 您需要添加server.port=8080 。
此時,您應該能夠通過在每個項目中(在單獨的終端窗口中)運行以下命令來啟動所有應用程序。
source ../https.env ./mvnw spring-boot:start在https://localhost:8080確認所有工作正常。 然后使用killall java殺死所有內容。
結合使用HTTPS和Docker Compose
Docker不讀取環境變量,也不了解您的本地CA(證書頒發機構),并且您不能將父目錄中的文件添加到映像中。
要解決此問題,您需要將keystore.p12和localhost.pem復制到每個項目的目錄中。 第一個將用于Spring Boot,第二個將被添加到每個映像上的Java Keystore中。
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" ]然后使用Spring Boot和HTTPS的環境變量創建一個.env文件。
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}您可以通過運行hostname來獲取{yourHostname}的值。
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以啟用Eureka注冊啟用HTTPS來重建所有Docker映像。 然后開始一切。
docker-compose up -d現在,您的所有應用程序都在帶有HTTPS的Docker中運行! 在https://localhost:8080證明。
如果您的應用程序無法啟動或無法彼此通信,請確保您的主機名與.env主機名匹配。
您可以進一步提高安全性:使用OAuth 2.0保護您的學校服務API。
OAuth 2.0的API安全性
將Okta Spring Boot Starter和Spring Cloud Config添加到school-service/pom.xml :
<dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-boot-starter</artifactId><version>1.1.0</version> </dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId> </dependency>然后在school-service/src/main/java/…?/service/configuration創建一個SecurityConfiguration.java類:
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 {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().oauth2ResourceServer().jwt();} }創建一個school-service/src/test/resources/test.properties文件并添加屬性,以便Okta的配置通過,并且在測試時不使用發現或配置服務器。
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 Config中讀取其配置。
eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:https://localhost:8761/eureka} spring:application:name: school-servicecloud:config:discovery:enabled: trueserviceId: CONFIGSERVERfailFast: 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進行安全的服務器到服務器通信 。
您需要做的最后一步是修改SchoolController (在school-ui項目中),以向其對school-server的請求中添加OAuth 2.0訪問令牌。
例子1.向RestTemplate添加一個AccessToken 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<List<TeachingClassDto>> 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<List<TeachingClassDto>>() {});}private ClientHttpRequestInterceptor getBearerTokenInterceptor(String accessToken) {return (request, bytes, execution) -> {request.getHeaders().add("Authorization", "Bearer " + accessToken);return execution.execute(request, bytes);};} }而已! 由于school-ui和school-service使用相同的OIDC應用程序設置,因此服務器將識別并驗證訪問令牌(也是JWT),并允許訪問。
此時,您可以選擇使用./mvnw spring-boot:run或Docker Compose單獨運行所有應用程序。 后一種方法只需要幾個命令。
mvn clean install docker-compose down docker-compose up -d使用HTTP Basic Auth與Eureka和Spring Cloud Config進行安全的微服務通信
為了進一步提高微服務,Eureka Server和Spring Cloud Config之間的安全性,您可以添加HTTP基本身份驗證。 為此,您需要在config和discovery項目中都添加spring-boot-starter-security作為依賴項。 然后,您需要為每個參數指定一個spring.security.user.password并對其進行加密。 您可以在Spring Cloud Config的安全性文檔中了解有關如何執行此操作的更多信息。
在兩個項目中都配置了Spring Security之后,就可以調整URL以在其中包含用戶名和密碼。 例如,以下是在school-ui項目的bootstrap.yml :
eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:https://username:password@localhost:8761/eureka}您需要對docker-compose.yml的URL進行類似的調整。
增強您對Spring微服務,Docker和OAuth 2.0的了解
本教程向您展示了如何確保微服務體系結構中的服務到服務通信是安全的。 您學習了如何在所有地方使用HTTPS以及如何使用OAuth 2.0和JWT鎖定API。
您可以在oktadeveloper / okta-spring-microservices-https-example上的GitHub上找到此示例的源代碼。
如果您想進一步探討這些主題,我想您會喜歡以下博客文章:
- 構建Spring微服務并對其進行Dockerize生產
- 使用Spring Boot為Microbrews構建微服務架構
- 使用Spring Boot 2.0和OAuth 2.0構建并保護微服務
- 使用OAuth 2.0和JHipster開發微服務架構
- 使用Spring Boot和OAuth 2.0進行安全的服務器到服務器通信
這些博客文章有助于使本文中的所有內容都能正常工作:
- 使用Spring Cloud Netflix Eureka進行安全發現
- 讓我們加密保護的Spring Boot
有問題嗎? 在下面的評論中詢問他們! 如果您的問題與這篇文章無關,請將其發布到我們的開發者論壇 。
要獲取有關更多技術博客文章的通知, 請在Twitter上關注我們@oktadev或訂閱我們的YouTube頻道 。
“具有HTTPS和OAuth 2.0的安全的服務到服務的Spring微服務”最初于2019年3月7日發布在Okta開發者博客上。
“我喜歡編寫身份驗證和授權代碼。” ?從來沒有Java開發人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進行托管身份驗證,授權和多因素身份驗證。
翻譯自: https://www.javacodegeeks.com/2019/03/secure-service-spring-microservices.html
總結
以上是生活随笔為你收集整理的使用HTTPS和OAuth 2.0保护服务到服务的Spring微服务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑cpu型号区分(电脑cpu型号区分图
- 下一篇: spring集成jndi_Spring应