Pemula untuk bekerja dengan Spring Cloud

Halo semuanya!


Pada artikel ini, saya akan menunjukkan komponen dasar untuk membuat layanan mixer Reaktif RESTful menggunakan Spring WebFlux, Spring Security, Spring Cloud Netflix Eureka (Penemuan Layanan), Hystrix (Pemutus Sirkuit), Ribbon (Penyeimbang Beban Sisi Klien), Konfigurasi Eksternal (melalui repositori git) , Sleuth Spring Cloud, Spring Cloud Gateway, MongoDB Spring Boot Reaktif. Serta Spring Boot Admin dan Zipkin untuk pemantauan.


Ulasan ini dibuat setelah mempelajari buku-buku Spring Microservices in Action dan Hands-On Spring 5 Security untuk Aplikasi Reaktif.


Pada artikel ini, kita akan membuat aplikasi dasar dengan tiga pertanyaan: dapatkan daftar game, dapatkan daftar pemain, buat game dari id pemain, permintaan untuk memeriksa rollback (Hystrix fallback) jika menunggu lama untuk mendapat jawaban. Dan implementasi otentikasi melalui token JWT berdasarkan buku Hands-On Spring 5 Security untuk Aplikasi Reaktif.


Saya tidak akan menjelaskan bagaimana setiap aplikasi dibuat dalam IDE, karena artikel ini ditujukan untuk pengguna yang berpengalaman.


Struktur proyek


Teks alternatif


Proyek ini terdiri dari dua modul. Modul spring-servers dapat disalin dengan aman dari proyek ke proyek. Hampir tidak ada kode dan konfigurasi. Modul tictactoe-services berisi modul dan layanan microser dari aplikasi kita. Saya akan segera melihat bahwa menambahkan auth-module dan auth-module - domain-module ke layanan, saya melanggar salah satu prinsip arsitektur layanan-mikro tentang otonomi layanan-mikro. Tetapi pada tahap pengembangan modul ini, saya percaya bahwa ini adalah solusi yang paling optimal.


Konfigurasi Gradle


Saya suka ketika seluruh konfigurasi Gradle ada dalam satu file, jadi saya mengonfigurasi seluruh proyek dalam satu build.gradle .


build.gradle
 buildscript { ext { springBootVersion = '2.1.1.RELEASE' gradleDockerVersion = '0.20.1' } repositories { mavenCentral() maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") classpath("gradle.plugin.com.palantir.gradle.docker:gradle-docker:${gradleDockerVersion}") } } allprojects { group = 'com.tictactoe' apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' apply plugin: 'com.palantir.docker' apply plugin: 'com.palantir.docker-run' apply plugin: 'com.palantir.docker-compose' } docker.name = 'com.tictactoe' bootJar.enabled = false sourceCompatibility = 11 repositories { mavenCentral() maven { url "https://repo.spring.io/milestone" } } subprojects { ext['springCloudVersion'] = 'Greenwich.M3' sourceSets.configureEach { sourceSet -> tasks.named(sourceSet.compileJavaTaskName, { options.annotationProcessorGeneratedSourcesDirectory = file("$buildDir/generated/sources/annotationProcessor/java/${sourceSet.name}") }) } repositories { mavenCentral() maven { url "https://repo.spring.io/milestone" } } dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compileOnly('org.projectlombok:lombok') annotationProcessor('org.projectlombok:lombok') } } project(':spring-servers') { bootJar.enabled = false task cleanAll { dependsOn subprojects*.tasks*.findByName('clean') } task buildAll { dependsOn subprojects*.tasks*.findByName('build') } dockerCompose { template 'docker-compose.spring-servers.template.yml' dockerComposeFile 'docker-compose.spring-servers.yml' } } project(':tictactoe-services') { bootJar.enabled = false task cleanAll { dependsOn subprojects*.tasks*.findByName('clean') } task buildAll { dependsOn subprojects*.tasks*.findByName('build') } } // Tictactoe Modules project(':tictactoe-services:domain-module') { bootJar.enabled = false jar { enabled = true group 'com.tictactoe' baseName = 'domain-module' version = '1.0' } dependencies { implementation('org.springframework.boot:spring-boot-starter-security') implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive') implementation('org.springframework.boot:spring-boot-starter-validation') implementation('com.fasterxml.jackson.core:jackson-annotations:2.9.3') implementation 'com.intellij:annotations:+@jar' compileOnly('org.projectlombok:lombok') testCompile group: 'junit', name: 'junit', version: '4.12' } } project(':tictactoe-services:auth-module') { bootJar.enabled = false jar { enabled = true baseName = 'auth-module' version = '1.0' } dependencies { implementation project(':tictactoe-services:domain-module') implementation('org.springframework.boot:spring-boot-starter-webflux') implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive') implementation('org.springframework.boot:spring-boot-starter-security') implementation('org.springframework.cloud:spring-cloud-starter-netflix-ribbon') implementation('org.springframework.security:spring-security-oauth2-core') implementation('org.springframework.security:spring-security-oauth2-jose') implementation 'com.intellij:annotations:+@jar' testImplementation('org.springframework.boot:spring-boot-starter-test') testImplementation('io.projectreactor:reactor-test') testImplementation('org.springframework.security:spring-security-test') } } project(':tictactoe-services:user-service') { bootJar { launchScript() baseName = 'user-service' version = '0.1.0' } dependencies { implementation project(':tictactoe-services:domain-module') implementation project(':tictactoe-services:auth-module') } } project(':tictactoe-services:game-service') { bootJar { launchScript() baseName = 'game-service' version = '0.1.0' } dependencies { implementation project(':tictactoe-services:domain-module') implementation project(':tictactoe-services:auth-module') } } project(':tictactoe-services:webapi-service') { bootJar { launchScript() baseName = 'webapi-service' version = '0.1.0' } dependencies { implementation project(':tictactoe-services:domain-module') implementation project(':tictactoe-services:auth-module') } } // Spring Servers project(':spring-servers:discovery-server') { bootJar { launchScript() baseName = 'discovery-server' version = '0.1.0' } dependencies { implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-server') implementation('org.springframework.boot:spring-boot-starter-security') compile('javax.xml.bind:jaxb-api:2.3.0') compile('javax.activation:activation:1.1') compile('org.glassfish.jaxb:jaxb-runtime:2.3.0') testImplementation('org.springframework.boot:spring-boot-starter-test') } } project(':spring-servers:config-server') { bootJar { launchScript() baseName = 'config-server' version = '0.1.0' } dependencies { implementation('org.springframework.boot:spring-boot-starter-security') implementation('org.springframework.cloud:spring-cloud-config-server') implementation('org.springframework.cloud:spring-cloud-starter-config') implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') testImplementation('org.springframework.boot:spring-boot-starter-test') } } project(':spring-servers:gateway-server') { bootJar { launchScript() baseName = 'gateway-server' version = '0.1.0' } dependencies { implementation('org.springframework.boot:spring-boot-starter-webflux') implementation('org.springframework.boot:spring-boot-starter-actuator') implementation('org.springframework.cloud:spring-cloud-starter-gateway') implementation('org.springframework.cloud:spring-cloud-starter-config') implementation('org.springframework.cloud:spring-cloud-starter-netflix-ribbon') implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') testImplementation('org.springframework.boot:spring-boot-starter-test') } } project(':spring-servers:admin-server') { ext['springBootAdminVersion'] = '2.1.1' bootJar { launchScript() baseName = 'admin-server' version = '0.1.0' } dependencies { implementation('org.springframework.boot:spring-boot-starter-web') implementation('org.springframework.boot:spring-boot-starter-security') implementation('de.codecentric:spring-boot-admin-starter-server') implementation('org.springframework.cloud:spring-cloud-starter-config') implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') testImplementation('org.springframework.boot:spring-boot-starter-test') testImplementation('org.springframework.security:spring-security-test') } dependencyManagement { imports { mavenBom "de.codecentric:spring-boot-admin-dependencies:${springBootAdminVersion}" } } } subprojects { subproject -> if (file("${subproject.projectDir}/docker/Dockerfile").exists()) { docker { // workingbit - replace with your dockerhub's username name "workingbit/${subproject.group}.${subproject.bootJar.baseName}" tags 'latest' dockerfile file("${subproject.projectDir}/docker/Dockerfile") files tasks.bootJar.archivePath, 'docker/run.sh' buildArgs "JAR_FILE": "${subproject.bootJar.baseName}-${subproject.bootJar.version}.jar", "RUN_SH": "run.sh" } } else { docker.name = 'noop' } if (subproject.name.endsWith('service')) { dependencies { implementation('org.springframework.boot:spring-boot-starter-actuator') implementation('org.springframework.boot:spring-boot-starter-webflux') implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive') implementation('org.springframework.boot:spring-boot-starter-security') implementation('org.springframework.security:spring-security-oauth2-core') implementation('org.springframework.security:spring-security-oauth2-jose') implementation('org.springframework.cloud:spring-cloud-starter-config') implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') implementation('org.springframework.cloud:spring-cloud-starter-netflix-hystrix') implementation('org.springframework.cloud:spring-cloud-starter-netflix-ribbon') implementation('org.springframework.cloud:spring-cloud-starter-sleuth') implementation('org.springframework.cloud:spring-cloud-starter-zipkin') implementation('org.springframework.security:spring-security-rsa') implementation('com.intellij:annotations:+@jar') implementation('org.apache.commons:commons-lang3:3.8.1') runtimeOnly('org.springframework.boot:spring-boot-devtools') testImplementation('org.springframework.boot:spring-boot-starter-test') testImplementation('de.flapdoodle.embed:de.flapdoodle.embed.mongo') testImplementation('io.projectreactor:reactor-test') } } } 

Menggunakan file konfigurasi umum memungkinkan Anda untuk membuat dependensi umum untuk layanan microser, dalam hal ini layanan dengan nama yang berakhiran "layanan", di satu tempat. TAPI, ini lagi-lagi melanggar prinsip otonomi dari layanan-layanan mikro. Selain dependensi umum, Anda dapat menambahkan tugas ke subproyek. Saya menambahkan gradle.plugin.com.palantir.gradle.docker:gradle-docker tugas plugin gradle.plugin.com.palantir.gradle.docker:gradle-docker untuk bekerja dengan Docker .


Modul otomatis


Sekarang, pertimbangkan modul otentikasi JWT. Deskripsi paket auth dari modul ini dapat ditemukan di buku otentikasi reaktif yang saya sebutkan di atas.


Teks alternatif


Ah, config bahas paket config secara lebih rinci.


Kelas properti "kompleks" ApplicationClientsProperties.java


 @Data @Component @ConfigurationProperties("appclients") public class ApplicationClientsProperties { private List<ApplicationClient> clients = new ArrayList<>(); @Data public static class ApplicationClient { private String username; private String password; private String[] roles; } } 

Kelas ini berisi properti "kompleks" untuk konfigurasi basis data inMemory.


Kelas konfigurasi modul AuthModuleConfig.java


 @Data @Configuration @PropertySource("classpath:moduleConfig.yml") public class AuthModuleConfig { @Value("${tokenExpirationMinutes:60}") private Integer tokenExpirationMinutes; @Value("${tokenIssuer:workingbit-example.com}") private String tokenIssuer; @Value("${tokenSecret:secret}") // length minimum 256 bites private String tokenSecret; } 

Di file sumber daya, Anda harus menentukan variabel-variabel ini. Dalam konfigurasi saya, token mati setelah 10 jam.


Kelas Konfigurasi Pencocokan Filter MicroserviceServiceJwtAuthWebFilter.java


 public class MicroserviceServiceJwtAuthWebFilter extends JwtAuthWebFilter { private final String[] matchersStrings; public MicroserviceServiceJwtAuthWebFilter(JwtService jwtService, String[] matchersStrings) { super(jwtService); this.matchersStrings = matchersStrings; } @Override protected ServerWebExchangeMatcher getAuthMatcher() { List<ServerWebExchangeMatcher> matchers = Arrays.stream(this.matchersStrings) .map(PathPatternParserServerWebExchangeMatcher::new) .collect(Collectors.toList()); return ServerWebExchangeMatchers.matchers(new OrServerWebExchangeMatcher(matchers)); } } 

Selama konstruksi, filter ini melewati layanan untuk bekerja dengan JWT dan daftar jalur yang akan diproses oleh filter ini.


Reactive Spring Boot Security MicroserviceSpringSecurityWebFluxConfig.java kelas konfigurasi


 @ConditionalOnProperty(value = "microservice", havingValue = "true") @EnableReactiveMethodSecurity @PropertySource(value = "classpath:/application.properties") public class MicroserviceSpringSecurityWebFluxConfig { @Value("${whiteListedAuthUrls}") private String[] whiteListedAuthUrls; @Value("${jwtTokenMatchUrls}") private String[] jwtTokenMatchUrls; /** * Bean which configures whiteListed and JWT filter urls * Also it configures authentication for Actuator. Actuator takes configured AuthenticationManager automatically * which uses MapReactiveUserDetailsService to configure inMemory users */ @Bean public SecurityWebFilterChain springSecurityFilterChain( ServerHttpSecurity http, JwtService jwtService ) { MicroserviceServiceJwtAuthWebFilter userServiceJwtAuthWebFilter = new MicroserviceServiceJwtAuthWebFilter(jwtService, jwtTokenMatchUrls); http.csrf().disable(); http .authorizeExchange() .pathMatchers(whiteListedAuthUrls) .permitAll() .and() .authorizeExchange() .pathMatchers("/actuator/**").hasRole("SYSTEM") .and() .httpBasic() .and() .addFilterAt(userServiceJwtAuthWebFilter, SecurityWebFiltersOrder.AUTHENTICATION); return http.build(); } } 

Ada tiga penjelasan menarik di sini.


 @ConditionalOnProperty(value = "microservice", havingValue = "true") 

Anotasi yang menghubungkan modul ini tergantung pada variabel layanan mikro dalam file konfigurasi yang ditentukan dalam anotasi. Ini diperlukan untuk menonaktifkan pengecekan token umum di beberapa modul. Dalam aplikasi ini, ini adalah webapi-service yang memiliki implementasi sendiri dari kacang SecurityWebFilterChain .


 @PropertySource(value = "classpath:/application.properties") 

Anotasi ini juga memungkinkan Anda untuk mengambil properti dari layanan utama di mana modul ini diimpor. Dengan kata lain, variabel


 @Value("${whiteListedAuthUrls}") private String[] whiteListedAuthUrls; @Value("${jwtTokenMatchUrls}") private String[] jwtTokenMatchUrls; 

Ambil nilainya dari konfigurasi layanan mikro keturunan.


Dan, anotasi yang memungkinkan Anda untuk melampirkan anotasi keamanan seperti @PreAuthorize(โ€œhasRole('MY_ROLE')โ€)


 @EnableReactiveMethodSecurity 

Dan dalam modul ini, kacang SecurityWebFilterChain dibuat, yang mengkonfigurasi akses ke aktuator, url dan url yang diizinkan tempat token JWT diperiksa. Perlu dicatat bahwa akses ke filter token JWT harus terbuka.


Konfigurasi SpringWebFluxConfig.java


Dalam konfigurasi ini, MapReactiveUserDetailsService dibuat untuk mengkonfigurasi aktuator dan pengguna sistem lainnya dalam memori.


 @Bean @Primary public MapReactiveUserDetailsService userDetailsRepositoryInMemory() { List<UserDetails> users = applicationClients.getClients() .stream() .map(applicationClient -> User.builder() .username(applicationClient.getUsername()) .password(passwordEncoder().encode(applicationClient.getPassword())) .roles(applicationClient.getRoles()).build()) .collect(toList()); return new MapReactiveUserDetailsService(users); } 

ReactiveUserDetailsService yang diperlukan untuk menjahit repositori pengguna kami dengan Spring Security .


 @Bean public ReactiveUserDetailsService userDetailsRepository(UserRepository users) { return (email) -> users.findByEmail(email).cast(UserDetails.class); } 

Kacang untuk membuat klien WebClient untuk melakukan permintaan reaktif.


 @Bean public WebClient loadBalancedWebClientBuilder(JwtService jwtService) { return WebClient.builder() .filter(lbFunction) .filter(authorizationFilter(jwtService)) .build(); } private ExchangeFilterFunction authorizationFilter(JwtService jwtService) { return ExchangeFilterFunction .ofRequestProcessor(clientRequest -> ReactiveSecurityContextHolder.getContext() .map(securityContext -> ClientRequest.from(clientRequest) .header(HttpHeaders.AUTHORIZATION, jwtService.getHttpAuthHeaderValue(securityContext.getAuthentication())) .build())); } 

Selama pembuatan, dua filter ditambahkan. LoadBalancer dan filter yang mengambil instance Authentication dari konteks ReactiveSecurityContext dan membuat token darinya sehingga filter diautentikasi oleh server target dan diotorisasi sesuai.


Dan untuk kenyamanan bekerja dengan tipe dan tanggal MongoDB ObjectId , saya menambahkan bin kreasi objectMapper:


 @Bean @Primary ObjectMapper objectMapper() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.serializerByType(ObjectId.class, new ToStringSerializer()); builder.deserializerByType(ObjectId.class, new JsonDeserializer() { @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { Map oid = p.readValueAs(Map.class); return new ObjectId( (Integer) oid.get("timestamp"), (Integer) oid.get("machineIdentifier"), ((Integer) oid.get("processIdentifier")).shortValue(), (Integer) oid.get("counter")); } }); builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return builder.build(); } 

Layanan game Microservice


Layanan permainan microservice memiliki struktur berikut:


Teks alternatif


Seperti yang Anda lihat di dalamnya, hanya satu file konfigurasi ApplicationConfig


Configurator ApplicationConfig.java


 @Data @Configuration @EnableReactiveMongoRepositories("com.tictactoe.gameservice.repository") @Import({ApplicationClientsProperties.class, SpringWebFluxConfig.class, MicroserviceSpringSecurityWebFluxConfig.class}) public class ApplicationConfig { @Value("${userserviceUrl}") private String userServiceUrl; } 

Ini berisi variabel dengan alamat layanan user-service dan ada dua penjelasan menarik:


 @EnableReactiveMongoRepositories("com.tictactoe.gameservice.repository") 

Anotasi ini diperlukan untuk menunjukkan repositori MongoDB ke konfigurator.


 @Import({ApplicationClientsProperties.class, SpringWebFluxConfig.class, MicroserviceSpringSecurityWebFluxConfig.class}) 

Anotasi ini mengimpor konfigurasi dari auth-module .


Layanan GameService.java


Layanan ini hanya memiliki kode menarik berikut:


 @HystrixCommand public Flux<Game> getAllGames() { return gameRepository.findAll(); } @HystrixCommand(fallbackMethod = "buildFallbackAllGames", threadPoolKey = "licenseByOrgThreadPool", threadPoolProperties = {@HystrixProperty(name = "coreSize", value = "30"), @HystrixProperty(name = "maxQueueSize", value = "10")}, commandProperties = { @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "75"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "7000"), @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "15000"), @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "5")} ) public Flux<Game> getAllGamesLong() { // logger.debug("LicenseService.getLicensesByOrg Correlation id: {}", UserContextHolder.getContext().getCorrelationId()); randomlyRunLong(); return gameRepository.findAll(); } 

Metode ini secara acak melempar pengecualian dan Hystrix sesuai dengan anotasi mengembalikan hasil dari metode berikut:


 private Flux<Game> buildFallbackAllGames() { User fakeUserBlack = new User("fakeUserBlack", "password", Collections.emptyList()); User fakeUserWhite = new User("fakeUserBlack", "password", Collections.emptyList()); Game game = new Game(fakeUserBlack, fakeUserWhite); List<Game> games = List.of(game); return Flux.fromIterable(games); } 

Seperti yang dinyatakan dalam buku yang disebutkan di atas, jika ada yang rusak, maka mari kita tunjukkan cache atau data alternatif lebih baik daripada tidak sama sekali.


Layanan webapi microservice


Ini adalah semacam middleware antara Gateway dan microservices internal yang tidak terlihat dari luar. Tujuan dari layanan ini adalah untuk mendapatkan seleksi dari layanan lain dan membentuk respons kepada pengguna berdasarkan pada layanan tersebut.


Teks alternatif


Kami memulai ulasan dengan konfigurasi.


Konfigurasi SpringSecurityWebFluxConfig.java


 @Configuration @EnableReactiveMethodSecurity public class SpringSecurityWebFluxConfig { private static final String AUTH_TOKEN_PATH = "/auth/token"; @Value("${whiteListedAuthUrls}") private String[] whiteListedAuthUrls; @Value("${jwtTokenMatchUrls}") private String[] jwtTokenMatchUrls; @Bean @Primary public SecurityWebFilterChain systemSecurityFilterChain( ServerHttpSecurity http, JwtService jwtService, @Qualifier("userDetailsRepository") ReactiveUserDetailsService userDetailsService ) { 

Di sini kita membuat manajer otentikasi dari layanan userDetailsService , yang telah kita tentukan sebelumnya dalam auth-module .


  UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService); 

Dan kami membuat filter dengan manajer ini, dan juga menambahkan konverter instance Otentikasi untuk mendapatkan data pengguna yang dikodekan dalam x-www-form-urlencoded .


  AuthenticationWebFilter tokenWebFilter = new AuthenticationWebFilter(authenticationManager); tokenWebFilter.setServerAuthenticationConverter(exchange -> Mono.justOrEmpty(exchange) .filter(ex -> AUTH_TOKEN_PATH.equalsIgnoreCase(ex.getRequest().getPath().value())) .flatMap(ServerWebExchange::getFormData) .filter(formData -> !formData.isEmpty()) .map((formData) -> { String email = formData.getFirst("email"); String password = formData.getFirst("password"); return new UsernamePasswordAuthenticationToken(email, password); }) ); 

Kami menambahkan penangan otorisasi yang sukses, intinya adalah untuk menempatkan token JWT di header permintaan yang dihasilkan dari Authentication sehingga otentikasi hanya dapat dilakukan menggunakan token tamu yang valid.


  tokenWebFilter.setAuthenticationSuccessHandler(new JwtAuthSuccessHandler(jwtService)); MicroserviceServiceJwtAuthWebFilter webApiJwtServiceWebFilter = new MicroserviceServiceJwtAuthWebFilter(jwtService, jwtTokenMatchUrls); http.csrf().disable(); http .authorizeExchange() 

Kami menyelesaikan alamat dari daftar putih. Seperti yang saya tulis sebelumnya, alamat yang akan diproses oleh filter JWT juga harus dibuka


  .pathMatchers(whiteListedAuthUrls) .permitAll() .and() .authorizeExchange() 

Kami melindungi aktuator dan beberapa alamat dengan otentikasi dasar


  .pathMatchers("/actuator/**").hasRole("SYSTEM") .pathMatchers(HttpMethod.GET, "/url-protected/**").hasRole("GUEST") .pathMatchers(HttpMethod.POST, "/url-protected/**").hasRole("USER") .and() .httpBasic() .and() .authorizeExchange() 

Membuat akses token otentikasi wajib


  .pathMatchers(AUTH_TOKEN_PATH).authenticated() .and() 

Tambahkan filter. Untuk mengotentikasi dan memverifikasi token JWT.


  .addFilterAt(webApiJwtServiceWebFilter, SecurityWebFiltersOrder.AUTHENTICATION) .addFilterAt(tokenWebFilter, SecurityWebFiltersOrder.AUTHENTICATION); return http.build(); } 

Dan seperti yang saya tulis di atas, layanan ini menonaktifkan pemeriksaan token JWT, umum untuk layanan lain, dengan menentukan nilai variabel micoservice=false dalam file application.properites .


Penerbitan Token, pendaftaran, dan pengontrol otorisasi AuthController.java


Saya tidak akan menjelaskan pengontrol ini, karena ini murni spesifik.


Layanan WebApiService.java


Layanan ini dipanggil di WebApiMethodProtectedController.jav pengontrol dan memiliki anotasi yang menarik:


 @PreAuthorize("hasRole('GUEST')") public Flux<User> getAllUsers() { } 

Anotasi ini memungkinkan akses ke metode hanya untuk pengguna yang berwenang dengan peran tamu.


Cara menguji


Buat lingkungan:


Teks alternatif


Dapatkan token


Teks alternatif


Perbarui variabel TOKEN di lingkungan dengan token yang diterima.


Daftarkan pengguna baru


Teks alternatif


Setelah mendaftar, Anda akan menerima token pengguna. Berakhir dalam 10 jam. Saat kedaluwarsa, Anda perlu mendapatkan yang baru. Untuk melakukan ini, minta token tamu lagi, perbarui lingkungan dan jalankan permintaan


Teks alternatif


Selanjutnya, Anda bisa mendapatkan daftar pengguna, game, atau membuat game baru. Dan juga uji Hystrix, lihat konfigurasi layanan dan mengenkripsi variabel untuk repositori git.


Referensi


Source: https://habr.com/ru/post/id434810/


All Articles