日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

spring react_使用Spring Cloud Gateway保护React式微服务

發布時間:2023/12/3 javascript 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring react_使用Spring Cloud Gateway保护React式微服务 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

spring react

朋友不允許朋友寫用戶身份驗證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 數分鐘之內即可在任何應用程序中對用戶進行身份驗證,管理和保護。

所以你想完全React,是嗎? 大! React式編程是使您的應用程序更高效的一種越來越流行的方式。 響應式應用程序異步調用響應,而不是調用資源并等待響應。 這使他們可以釋放處理能力,僅在必要時執行處理,并且比其他系統更有效地擴展。

Java生態系統在React框架中占有相當大的份額,其中包括Play框架,Ratpack,Vert.x和Spring WebFlux。 像響應式編程一樣,微服務架構可以幫助大型團隊快速擴展,并可以使用上述任何出色的框架進行構建。

今天,我想向您展示如何使用Spring Cloud Gateway,Spring Boot和Spring WebFlux構建React性微服務架構。 我們將利用Spring Cloud Gateway,因為API網關通常是云原生微服務體系結構中的重要組件,為所有后端微服務提供了聚合層。

本教程向您展示如何使用REST API構建微服務,該API返回新車列表。 您將使用Eureka進行服務發現,并使用Spring Cloud Gateway將請求路由到微服務。 然后,您將集成Spring Security,以便只有經過身份驗證的用戶才能訪問您的API網關和微服務。

先決條件 : HTTPie (或cURL), Java 11+和Internet連接。

Spring Cloud Gateway與Zuul

Zuul是Netflix的API網關。 Zuul于2013年首次發布,最初并不具有React性,但Zuul 2是完全重寫后的版本,使其具有React性。 不幸的是,Spring Cloud 不支持Zuul 2 ,并且可能永遠不支持 。

現在,Spring Cloud Gateway是Spring Cloud Team首選的API網關實現。 它基于Spring 5,Reactor和Spring WebFlux構建。 不僅如此,它還包括斷路器集成,使用Eureka進行服務發現,并且與OAuth 2.0集成起來容易得多

讓我們深入。

創建一個Spring Cloud Eureka Server項目

首先創建一個目錄來保存所有項目,例如spring-cloud-gateway 。 在終端窗口中導航至它,并創建一個包含Spring Cloud Eureka Server作為依賴項的discovery-service項目。

http https://start.spring.io/starter.zip javaVersion==11 artifactId==discovery-service \name==eureka-service baseDir==discovery-service \dependencies==cloud-eureka-server | tar -xzvf -

上面的命令使用HTTPie 。 我強烈建議安裝它。 您也可以使用curl 。 運行curl https://start.spring.io以查看語法。

在其主類上添加@EnableEurekaServer ,以將其用作Eureka服務器。

import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer @SpringBootApplication public class EurekaServiceApplication {...}

將以下屬性添加到項目的src/main/resources/application.properties文件中,以配置其端口并關閉Eureka注冊。

server.port=8761 eureka.client.register-with-eureka=false

要使discovery-service在Java 11+上運行,請添加對JAXB的依賴關系。

<dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId> </dependency>

使用./mvnw spring-boot:run或通過在IDE中運行它來啟動項目。

創建一個Spring Cloud Gateway項目

接下來,創建一個包含一些Spring Cloud依賴項的api-gateway項目。

http https://start.spring.io/starter.zip javaVersion==11 artifactId==api-gateway \name==api-gateway baseDir==api-gateway \dependencies==actuator,cloud-eureka,cloud-feign,cloud-gateway,cloud-hystrix,webflux,lombok | tar -xzvf -

一分鐘后,我們將重新配置該項目。

使用Spring WebFlux創建React式微服務

汽車微服務將包含此示例代碼的很大一部分,因為它包含支持CRUD(創建,讀取,更新和刪除)的功能齊全的REST API。

使用start.spring.io創建car-service項目:

http https://start.spring.io/starter.zip javaVersion==11 artifactId==car-service \name==car-service baseDir==car-service \dependencies==actuator,cloud-eureka,webflux,data-mongodb-reactive,flapdoodle-mongo,lombok | tar -xzvf -

這個命令中的dependencies參數很有趣。 您可以看到其中包括Spring WebFlux,以及MongoDB。 Spring Data還為Redis和Cassandra提供了響應式驅動程序。

您可能還對R2DBC (React性關系數據庫連接)感興趣, 這是將React性編程API引入SQL數據庫的一種嘗試。 在本示例中,我沒有使用它,因為在start.spring.io上尚不可用。

使用Spring WebFlux構建REST API

我是大眾的忠實擁護者,尤其是經典的公交車和bug車。 您是否知道大眾在未來幾年內將推出大量電動汽車? 我對ID Buzz感到非常興奮! 它具有經典曲線,全電動。 它甚至擁有350+的馬力!

如果您不熟悉ID Buzz,這是大眾汽車的照片。

讓我們從這個API示例中獲得一些樂趣,并將電動VW用于我們的數據集。 該API將跟蹤各種汽車名稱和發布日期。

在src/main/java/…?/CarServiceApplication.java添加Eureka注冊,示例數據初始化和響應式REST API:

package com.example.carservice;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono;import java.time.LocalDate; import java.time.Month; import java.util.Set; import java.util.UUID;@EnableEurekaClient (1) @SpringBootApplication @Slf4j (2) public class CarServiceApplication {public static void main(String[] args) {SpringApplication.run(CarServiceApplication.class, args);}@Bean (3)ApplicationRunner init(CarRepository repository) {// Electric VWs from https://www.vw.com/electric-concepts/// Release dates from https://www.motor1.com/features/346407/volkswagen-id-price-on-sale/Car ID = new Car(UUID.randomUUID(), "ID.", LocalDate.of(2019, Month.DECEMBER, 1));Car ID_CROZZ = new Car(UUID.randomUUID(), "ID. CROZZ", LocalDate.of(2021, Month.MAY, 1));Car ID_VIZZION = new Car(UUID.randomUUID(), "ID. VIZZION", LocalDate.of(2021, Month.DECEMBER, 1));Car ID_BUZZ = new Car(UUID.randomUUID(), "ID. BUZZ", LocalDate.of(2021, Month.DECEMBER, 1));Set<Car> vwConcepts = Set.of(ID, ID_BUZZ, ID_CROZZ, ID_VIZZION);return args -> {repository.deleteAll() (4).thenMany(Flux.just(vwConcepts).flatMap(repository::saveAll)).thenMany(repository.findAll()).subscribe(car -> log.info("saving " + car.toString())); (5)};} }@Document @Data @NoArgsConstructor @AllArgsConstructor class Car { (6)@Idprivate UUID id;private String name;private LocalDate releaseDate; }interface CarRepository extends ReactiveMongoRepository<Car, UUID> { } (7)@RestController class CarController { (8)private CarRepository carRepository;public CarController(CarRepository carRepository) {this.carRepository = carRepository;}@PostMapping("/cars")@ResponseStatus(HttpStatus.CREATED)public Mono<Car> addCar(@RequestBody Car car) { (9)return carRepository.save(car);}@GetMapping("/cars")public Flux<Car> getCars() { (10)return carRepository.findAll();}@DeleteMapping("/cars/{id}")public Mono<ResponseEntity<Void>> deleteCar(@PathVariable("id") UUID id) {return carRepository.findById(id).flatMap(car -> carRepository.delete(car).then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))).defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));} }
  • 添加@EnableEurekaClient批注以進行服務發現
  • @Slf4j是Lombok的便捷注釋,可用于登錄類
  • ApplicationRunner bean用默認數據填充MongoDB
  • 刪除MongoDB中的所有現有數據,以免添加新數據
  • 訂閱結果,以便調用deleteAll()和saveAll()
  • 帶有Spring Data NoSQL和Lombok注釋的Car類,以減少樣板
  • CarRepository接口擴展了ReactiveMongoRepository ,幾乎沒有任何代碼,可為您提供CRUDability!
  • 使用CarRepository執行CRUD操作的CarController類
  • Spring WebFlux返回單個對象的Mono發布者
  • 返回多個對象的Flex發布者
  • 如果使用IDE構建項目,則需要為IDE設置Lombok 。

    您還需要修改car-service項目的application.properties以設置其名稱和端口。

    spring.application.name=car-service server.port=8081

    運行MongoDB

    運行MongoDB的最簡單方法是從car-service/pom.xml的flappoodle依賴項中刪除test范圍。 這將導致您的應用程序啟動嵌入式MongoDB依賴關系。

    <dependency><groupId>de.flapdoodle.embed</groupId><artifactId>de.flapdoodle.embed.mongo</artifactId><!--<scope>test</scope>--> </dependency>

    您還可以使用Homebrew安裝和運行MongoDB。

    brew tap mongodb/brew brew install mongodb-community@4.2 mongod

    或者,使用Docker:

    docker run -d -it -p 27017:27017 mongo

    使用WebFlux傳輸數據

    這樣就完成了使用Spring WebFlux構建REST API所需完成的所有工作。

    “可是等等!” 你可能會說。 “我以為WebFlux就是關于流數據的?”

    在此特定示例中,您仍然可以從/cars端點流式傳輸數據,但不能在瀏覽器中。

    除了使用服務器發送事件或WebSocket之外,瀏覽器無法使用流。 但是,非瀏覽器客戶端可以通過發送具有application/stream+json值的Accept報頭來獲取JSON流(感謝Rajeev Singh的提示)。

    您可以通過啟動瀏覽器并使用HTTPie發出請求來測試此時一切正常。 但是,編寫自動化測試要好得多!

    使用WebTestClient測試您的WebFlux API

    WebClient是Spring WebFlux的一部分,可用于發出響應請求,接收響應以及使用有效負載填充對象。 伴隨類WebTestClient可用于測試WebFlux API。 它包含與WebClient相似的請求方法,以及檢查響應正文,狀態和標頭的方法。

    修改car-service項目中的src/test/java/…?/CarServiceApplicationTests.java類以包含以下代碼。

    package com.example.carservice;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Mono;import java.time.LocalDate; import java.time.Month; import java.util.Collections; import java.util.UUID;@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,properties = {"spring.cloud.discovery.enabled = false"}) public class CarServiceApplicationTests {@AutowiredCarRepository carRepository;@AutowiredWebTestClient webTestClient;@Testpublic void testAddCar() {Car buggy = new Car(UUID.randomUUID(), "ID. BUGGY", LocalDate.of(2022, Month.DECEMBER, 1));webTestClient.post().uri("/cars").contentType(MediaType.APPLICATION_JSON_UTF8).accept(MediaType.APPLICATION_JSON_UTF8).body(Mono.just(buggy), Car.class).exchange().expectStatus().isCreated().expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8).expectBody().jsonPath("$.id").isNotEmpty().jsonPath("$.name").isEqualTo("ID. BUGGY");}@Testpublic void testGetAllCars() {webTestClient.get().uri("/cars").accept(MediaType.APPLICATION_JSON_UTF8).exchange().expectStatus().isOk().expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8).expectBodyList(Car.class);}@Testpublic void testDeleteCar() {Car buzzCargo = carRepository.save(new Car(UUID.randomUUID(), "ID. BUZZ CARGO",LocalDate.of(2022, Month.DECEMBER, 2))).block();webTestClient.delete().uri("/cars/{id}", Collections.singletonMap("id", buzzCargo.getId())).exchange().expectStatus().isOk();} }

    為了證明它有效,請運行./mvnw test 。 測試通過后,請拍一下自己的背!

    如果您使用的是Windows,請使用mvnw test 。

    將Spring Cloud Gateway與響應式微服務一起使用

    要在同一IDE窗口中編輯所有三個項目,我發現創建一個聚合器pom.xml很有用。 在項目的父目錄中創建pom.xml文件,然后將下面的XML復制到其中。

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.okta.developer</groupId><artifactId>reactive-parent</artifactId><version>1.0.0-SNAPSHOT</version><packaging>pom</packaging><name>reactive-parent</name><modules><module>discovery-service</module><module>car-service</module><module>api-gateway</module></modules> </project>

    創建此文件后,您應該能夠在IDE中將其作為項目打開,并可以輕松地在項目之間導航。

    在api-gateway項目中,將@EnableEurekaClient添加到主類以使其能夠感知Eureka。

    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@EnableEurekaClient @SpringBootApplication public class ApiGatewayApplication {...}

    然后,修改src/main/resources/application.properties文件以配置應用程序名稱。

    spring.application.name=gateway

    在ApiGatewayApplication創建一個RouteLocator bean,以配置路由。 您可以使用YAML配置Spring Cloud Gateway,但我更喜歡Java。

    package com.example.apigateway;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean;@EnableEurekaClient @SpringBootApplication public class ApiGatewayApplication {public static void main(String[] args) {SpringApplication.run(ApiGatewayApplication.class, args);}@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder builder) {return builder.routes().route("car-service", r -> r.path("/cars").uri("lb://car-service")).build();} }

    更改完這些代碼后,您應該能夠啟動所有三個Spring Boot應用程序并點擊http://localhost:8080/cars 。

    $ http :8080/cars HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 transfer-encoding: chunked[{"id": "ff48f617-6cba-477c-8e8f-2fc95be96416","name": "ID. CROZZ","releaseDate": "2021-05-01"},{"id": "dd6c3c32-724c-4511-a02c-3348b226160a","name": "ID. BUZZ","releaseDate": "2021-12-01"},{"id": "97cfc577-d66e-4a3c-bc40-e78c3aab7261","name": "ID.","releaseDate": "2019-12-01"},{"id": "477632c8-2206-4f72-b1a8-e982e6128ab4","name": "ID. VIZZION","releaseDate": "2021-12-01"} ]

    添加REST API來檢索您喜歡的汽車

    創建一個/fave-cars端點,以/fave-cars不是您最喜歡的汽車。

    首先,添加一個負載平衡的WebClient.Builder bean。

    @Bean @LoadBalanced public WebClient.Builder loadBalancedWebClientBuilder() {return WebClient.builder(); }

    然后在同一文件中的ApiGatewayApplication類下添加Car POJO和FaveCarsController 。

    public class ApiGatewayApplication {...} class Car {...} class FaveCarsController {...}

    使用WebClient檢索汽車并過濾掉您不喜歡的汽車。

    @Data class Car {private String name;private LocalDate releaseDate; }@RestController class FaveCarsController {private final WebClient.Builder carClient;public FaveCarsController(WebClient.Builder carClient) {this.carClient = carClient;}@GetMapping("/fave-cars")public Flux<Car> faveCars() {return carClient.build().get().uri("lb://car-service/cars").retrieve().bodyToFlux(Car.class).filter(this::isFavorite);}private boolean isFavorite(Car car) {return car.getName().equals("ID. BUZZ");} }

    如果您沒有使用自動為您導入的IDE,則需要將以下內容復制/粘貼到ApiGatewayApplication.java的頂部:

    import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux;

    重新啟動網關應用程序以查看http://localhost:8080/fave-cars終結點僅返回ID Buzz。

    Hystrix的故障轉移呢?

    在撰寫本文時,Spring Cloud Gateway 僅支持Hystrix 。 Spring Cloud不贊成直接支持Hystrix,而是使用Spring Cloud Breaker 。 不幸的是,該庫尚未發布GA版本,因此我決定不使用它。

    要將Hystrix與Spring Cloud Gateway結合使用,可以向car-service路線添加過濾器,如下所示:

    .route("car-service", r -> r.path("/cars").filters(f -> f.hystrix(c -> c.setName("carsFallback").setFallbackUri("forward:/cars-fallback"))).uri("lb://car-service/cars")) .build();

    然后創建一個CarsFallback控制器來處理/cars-fallback路由。

    @RestController class CarsFallback {@GetMapping("/cars-fallback")public Flux<Car> noCars() {return Flux.empty();} }

    首先,重新啟動網關,并確認http://localhost:8080/cars可以正常工作。 然后關閉汽車服務,再試一次,您會看到它現在返回一個空數組。 重新啟動汽車服務,您將再次看到該列表。

    您已經使用Spring Cloud Gateway和Spring WebFlux構建了彈性和React性的微服務架構。 現在讓我們看看如何保護它!

    Feign with Spring Cloud Gateway怎么樣?

    如果您想在WebFlux應用程序中使用Feign,請參閱feign - active項目。 在這個特定示例中,我不需要Feign。

    帶有OAuth 2.0的安全Spring Cloud Gateway

    OAuth 2.0是用于委托訪問API的授權框架。 OIDC(或OpenID Connect)是OAuth 2.0之上的薄層,可提供身份驗證。 Spring Security對這兩個框架都有出色的支持,Okta也是如此。

    您可以通過構建自己的服務器或使用開源實現來在沒有云身份提供商的情況下使用OAuth 2.0和OIDC。 但是,難道您不希望只使用諸如Okta之類一直在線的東西嗎?

    如果您已經有Okta帳戶,請參見下面的在Okta中創建Web應用程序 。 否則,我們創建了一個Maven插件,該插件配置了一個免費的Okta開發者帳戶+一個OIDC應用程序(不到一分鐘!)。

    要使用它,請運行: ./mvnw com.okta:okta-maven-plugin:setup : ./mvnw com.okta:okta-maven-plugin:setup創建一個帳戶并配置您的Spring Boot應用程序以與Okta一起使用。

    在Okta中創建Web應用程序

    登錄到您的1563開發者帳戶(或者注冊 ,如果你沒有一個帳戶)。

  • 在“ 應用程序”頁面上,選擇添加應用程序
  • 在“創建新應用程序”頁面上,選擇“ Web”
  • 為您的應用提供一個令人難忘的名稱,將http://localhost:8080/login/oauth2/code/okta為登錄重定向URI,選擇刷新令牌 (除了授權代碼 ),然后點擊完成
  • 將發行者(位于API > 授權服務器下 ),客戶端ID和客戶端密鑰復制到兩個項目的application.properties中。

    okta.oauth2.issuer=$issuer okta.oauth2.client-id=$clientId okta.oauth2.client-secret=$clientSecret

    接下來,將Okta Spring Boot啟動器和Spring Cloud Security添加到網關的pom.xml :

    <dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-boot-starter</artifactId><version>1.2.1</version> </dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-security</artifactId> </dependency>

    這就是添加Okta OIDC登錄所需要做的一切! 重新啟動您的Gateway應用,并在瀏覽器中導航到http://localhost:8080/fave-cars ,以將其重定向到Okta以進行用戶授權。

    使您的網關成為OAuth 2.0資源服務器

    您可能不會在網關本身上為您的應用程序構建UI。 您可能會改用SPA或移動應用程序。 要將網關配置為充當資源服務器(查找帶有承載令牌的Authorization標頭),請在與主類相同的目錄中添加一個新的SecurityConfiguration類。

    package com.example.apigateway;import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain;@EnableWebFluxSecurity @EnableReactiveMethodSecurity public class SecurityConfiguration {@Beanpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {// @formatter:offhttp.authorizeExchange().anyExchange().authenticated().and().oauth2Login().and().oauth2ResourceServer().jwt();return http.build();// @formatter:on} }

    帶有Spring Cloud Gateway的CORS

    如果您將SPA用于UI,則還需要配置CORS。 您可以通過向CorsWebFilter添加CorsWebFilter bean來實現。

    @Bean CorsWebFilter corsWebFilter() {CorsConfiguration corsConfig = new CorsConfiguration();corsConfig.setAllowedOrigins(List.of("*"));corsConfig.setMaxAge(3600L);corsConfig.addAllowedMethod("*");corsConfig.addAllowedHeader("*");UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", corsConfig);return new CorsWebFilter(source); }

    確保您的進口商品與以下商品相符。

    import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

    Spring Cloud Gateway的文檔介紹了如何使用YAML或WebFluxConfigurer配置CORS。 不幸的是,我無法任其工作。

    使用WebTestClient和JWT測試網關

    如果您在網關中配置了CORS,則可以測試它是否可以與WebTestClient一起使用。 用以下代碼替換ApiGatewayApplicationTests的代碼。

    import java.util.Map; import java.util.function.Consumer;import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when;@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,properties = {"spring.cloud.discovery.enabled = false"}) public class ApiGatewayApplicationTests {@AutowiredWebTestClient webTestClient;@MockBean (1)ReactiveJwtDecoder jwtDecoder;@Testpublic void testCorsConfiguration() {Jwt jwt = jwt(); (2)when(this.jwtDecoder.decode(anyString())).thenReturn(Mono.just(jwt)); (3)WebTestClient.ResponseSpec response = webTestClient.put().uri("/").headers(addJwt(jwt)) (4).header("Origin", "http://example.com").exchange();response.expectHeader().valueEquals("Access-Control-Allow-Origin", "*");}private Jwt jwt() {return new Jwt("token", null, null,Map.of("alg", "none"), Map.of("sub", "betsy"));}private Consumer<HttpHeaders> addJwt(Jwt jwt) {return headers -> headers.setBearerAuth(jwt.getTokenValue());} }
  • 模擬ReactiveJwtDecoder以便您設置期望值并在解碼時返回模擬
  • 創建一個新的JWT
  • 解碼后返回相同的JWT
  • 將JWT添加到帶有Bearer前綴的Authorization標頭中
  • 我喜歡WebTestClient如何讓您如此輕松地設置安全標頭!

    您已將Spring Cloud Gateway配置為使用OIDC登錄并充當OAuth 2.0資源服務器,但是car服務仍在端口8081上可用。 我們修復一下,以便只有網關可以與它進行對話。

    微服務通信的安全網關

    將Okta Spring Boot啟動器添加到car-service/pom.xml :

    <dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-boot-starter</artifactId><version>1.2.1</version> </dependency>

    將okta.*屬性從網關的application.properties復制到汽車服務的屬性。 然后創建一個SecurityConfiguration類,使該應用程序成為OAuth 2.0資源服務器。

    package com.example.carservice;import com.okta.spring.boot.oauth.Okta; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain;@EnableWebFluxSecurity @EnableReactiveMethodSecurity public class SecurityConfiguration {@Beanpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {// @formatter:offhttp.authorizeExchange().anyExchange().authenticated().and().oauth2ResourceServer().jwt();Okta.configureResourceServer401ResponseBody(http);return http.build();// @formatter:on} }

    而已! 重新啟動您的汽車服務應用程序,現在它已受到匿名入侵者的保護。

    $ http :8081/cars HTTP/1.1 401 Unauthorized Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: text/plain ...401 Unauthorized

    使用WebTestClient和JWT測試您的微服務

    啟用安全性后,您在car-service項目中添加的測試將不再起作用。 修改CarServiceApplicationTests.java的代碼,以將JWT訪問令牌添加到每個請求。

    package com.example.carservice;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Mono;import java.time.LocalDate; import java.time.Month; import java.util.Map; import java.util.UUID; import java.util.function.Consumer;import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when;@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,properties = {"spring.cloud.discovery.enabled = false"}) public class CarServiceApplicationTests {@AutowiredCarRepository carRepository;@AutowiredWebTestClient webTestClient;@MockBeanReactiveJwtDecoder jwtDecoder;@Testpublic void testAddCar() {Car buggy = new Car(UUID.randomUUID(), "ID. BUGGY", LocalDate.of(2022, Month.DECEMBER, 1));Jwt jwt = jwt();when(this.jwtDecoder.decode(anyString())).thenReturn(Mono.just(jwt));webTestClient.post().uri("/cars").contentType(MediaType.APPLICATION_JSON_UTF8).accept(MediaType.APPLICATION_JSON_UTF8).headers(addJwt(jwt)).body(Mono.just(buggy), Car.class).exchange().expectStatus().isCreated().expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8).expectBody().jsonPath("$.id").isNotEmpty().jsonPath("$.name").isEqualTo("ID. BUGGY");}@Testpublic void testGetAllCars() {Jwt jwt = jwt();when(this.jwtDecoder.decode(anyString())).thenReturn(Mono.just(jwt));webTestClient.get().uri("/cars").accept(MediaType.APPLICATION_JSON_UTF8).headers(addJwt(jwt)).exchange().expectStatus().isOk().expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8).expectBodyList(Car.class);}@Testpublic void testDeleteCar() {Car buzzCargo = carRepository.save(new Car(UUID.randomUUID(), "ID. BUZZ CARGO",LocalDate.of(2022, Month.DECEMBER, 2))).block();Jwt jwt = jwt();when(this.jwtDecoder.decode(anyString())).thenReturn(Mono.just(jwt));webTestClient.delete().uri("/cars/{id}", Map.of("id", buzzCargo.getId())).headers(addJwt(jwt)).exchange().expectStatus().isOk();}private Jwt jwt() {return new Jwt("token", null, null,Map.of("alg", "none"), Map.of("sub", "dave"));}private Consumer<HttpHeaders> addJwt(Jwt jwt) {return headers -> headers.setBearerAuth(jwt.getTokenValue());} }

    再次運行測試,一切都會通過!

    Spring Security 5.2中的模擬JWT支持

    感謝Josh Cummings在JWT和WebTestClient方面的幫助。 Josh預覽了Spring Security 5.2中的模擬JWT支持。

    this.webTestClient.mutateWith(jwt()).post(...)

    Josh還提供了一個示例測試,展示了如何模擬JWT的主題,范圍和聲明 。 該代碼基于Spring Security 5.2.0.M3中的新功能。

    Spring Security領域中的OAuth 2.0和JWT支持前景光明! 😎

    中繼訪問令牌:網關到微服務

    您只需為網關與該受保護的服務進行一個小小的更改即可。 這非常簡單,我??!

    在ApiGatewayApplication.java ,添加一個過濾器,該過濾器將應用來自Spring Cloud Security的TokenRelayGatewayFilterFactory 。

    import org.springframework.cloud.security.oauth2.gateway.TokenRelayGatewayFilterFactory;@Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder,TokenRelayGatewayFilterFactory filterFactory) {return builder.routes().route("car-service", r -> r.path("/cars").filters(f -> f.filter(filterFactory.apply())).uri("lb://car-service/cars")).build(); }

    該中繼工廠尚未自動刷新訪問令牌 。

    重新啟動您的API網關,您應該能夠查看http://localhost:8080/cars并使一切正常運行。

    很可愛,你不覺得嗎?

    了解有關Spring的更多信息Spring網關和響應式微服務

    我幾乎沒有涉及Spring Cloud Gateway的功能。 如果您要構建響應式微服務,建議您看看它。

    請參閱Spring Cloud Gateway項目頁面以獲取更多信息,包括文檔。 我還發現這些教程很有用:

    • Spring Cloud Gateway入門 – 2019年6月18日
    • Spring Cloud Gateway教程 – 2019年5月30日

    您可以在spring-cloud-gateway目錄的@ oktadeveloper / java-microservices-examples中找到此示例的源代碼。

    git clone https://github.com/oktadeveloper/java-microservices-examples.git cd java-microservices-examples/spring-cloud-gateway

    要了解有關使用Java和Spring進行微服務和React式編程的更多信息,請查看這些文章。

    • 帶有Spring Boot和Spring Cloud的Java微服務
    • 帶有Spring Cloud Config和JHipster的Java微服務
    • 通過Java,Docker和Spring Boot獲得Jibby
    • 構建Spring微服務并對其進行Dockerize生產
    • 使用Spring WebFlux構建React性API

    如果您喜歡本教程, 請在Twitter上關注@oktadev 。 我們還會定期將截屏視頻發布到我們的YouTube頻道 。

    帶有Spring Cloud Gateway的安全React微服務最初于2019年8月28日發布在Okta開發者博客上。

    朋友不允許朋友寫用戶身份驗證。 厭倦了管理自己的用戶? 立即嘗試Okta的API和Java SDK。 數分鐘之內即可在任何應用程序中對用戶進行身份驗證,管理和保護。

    翻譯自: https://www.javacodegeeks.com/2019/10/secure-reactive-microservices-with-spring-cloud-gateway.html

    spring react

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的spring react_使用Spring Cloud Gateway保护React式微服务的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。