Starter für die Arbeit mit Spring Cloud

Hallo allerseits!


In diesem Artikel werde ich die grundlegenden Komponenten zum Erstellen reaktiver RESTful-Mixer-Services mit Spring WebFlux, Spring Security, Spring Cloud Netflix Eureka (Serviceerkennung), Hystrix (Leistungsschalter), Multifunktionsleiste (clientseitiger Lastausgleich) und externer Konfiguration (über das Git-Repository) demonstrieren. , Spring Cloud Sleuth, Spring Cloud Gateway und Spring Boot Reactive MongoDB. Sowie Spring Boot Admin und Zipkin zur Überwachung.


Diese Überprüfung wurde nach dem Studium der Bücher Spring Microservices in Aktion und Hands-On Spring 5 Security für reaktive Anwendungen durchgeführt.


In diesem Artikel erstellen wir eine elementare Anwendung mit drei Abfragen: Eine Liste der Spiele abrufen, eine Liste der Spieler abrufen, ein Spiel aus der ID der Spieler erstellen, eine Anforderung zum Überprüfen des Rollbacks (Hystrix-Fallback) im Falle eines langen Wartens auf eine Antwort. Und die Implementierung der Authentifizierung durch das JWT-Token basierend auf dem Buch Hands-On Spring 5 Security for Reactive Applications.


Ich werde nicht beschreiben, wie jede Anwendung in der IDE erstellt wird, da dieser Artikel für einen erfahrenen Benutzer gedacht ist.


Projektstruktur


Alternativtext


Das Projekt besteht aus zwei Modulen. Das spring-servers Modul kann sicher von Projekt zu Projekt kopiert werden. Es gibt fast keinen Code und keine Konfigurationen. Das tictactoe-services Modul enthält die Module und Microservices unserer Anwendung. Ich werde sofort feststellen, dass das Hinzufügen von auth-module und domain-module zu den Diensten gegen eines der Prinzipien der Microservice-Architektur bezüglich der Autonomie von Microservices verstößt. In der Phase der Entwicklung dieser Module glaube ich jedoch, dass dies die optimalste Lösung ist.


Gradle-Konfiguration


Ich mag es, wenn sich die gesamte Gradle-Konfiguration in einer Datei befindet, also habe ich das gesamte Projekt in einem 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') } } } 

Durch die Verwendung einer allgemeinen Konfigurationsdatei können Sie an einer Stelle Abhängigkeiten festlegen, die Microservices gemeinsam sind, in diesem Fall Dienste mit einem Namen, der auf "Dienst" endet. ABER dies verstößt erneut gegen das Prinzip der Autonomie von Mikrodiensten. Zusätzlich zu allgemeinen Abhängigkeiten können Sie Teilprojekten Aufgaben hinzufügen. Ich habe gradle.plugin.com.palantir.gradle.docker:gradle-docker hinzugefügt gradle.plugin.com.palantir.gradle.docker:gradle-docker Plugin-Aufgaben, um mit Docker zu arbeiten.


Auth-Modul


Betrachten Sie nun das JWT-Authentifizierungsmodul. Eine Beschreibung des auth Pakets dieses Moduls finden Sie im oben erwähnten Reactive Authentication Book.


Alternativtext


Ah, config das config genauer betrachten.


Die Klasse der „komplexen“ Eigenschaften 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; } } 

Diese Klasse enthält „komplexe“ Eigenschaften für die Konfiguration der inMemory-Datenbank.


Konfigurationsklasse des AuthModuleConfig.java-Moduls


 @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; } 

In der Ressourcendatei müssen Sie diese Variablen angeben. In meiner Konfiguration wird der Token nach 10 Stunden gelöscht.


MicroserviceServiceJwtAuthWebFilter.java Filter Matchers Konfigurationsklasse


 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)); } } 

Während der Erstellung übergibt dieser Filter den Dienst für die Arbeit mit JWT und eine Liste der Pfade, die dieser Filter verarbeiten wird.


Reaktive Spring Boot-Sicherheit MicroserviceSpringSecurityWebFluxConfig.java-Konfigurationsklasse


 @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(); } } 

Hier gibt es drei interessante Anmerkungen.


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

Eine Annotation, die dieses Modul abhängig von der Microservice-Variablen in der Konfigurationsdatei verbindet, die in der Annotation angegeben ist. Dies ist erforderlich, um die allgemeine Tokenprüfung in einigen Modulen zu deaktivieren. In dieser Anwendung ist dies der webapi-service , der eine eigene Implementierung der SecurityWebFilterChain Bean hat.


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

Mit dieser Anmerkung können Sie auch Eigenschaften aus dem Hauptdienst übernehmen, in den dieses Modul importiert wird. Mit anderen Worten, Variablen


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

Nehmen Sie ihre Werte aus der Microservice-Konfiguration des Nachkommen.


Und eine Anmerkung, mit der Sie Sicherheitsanmerkungen wie @PreAuthorize(“hasRole('MY_ROLE')”)


 @EnableReactiveMethodSecurity 

In diesem Modul wird die SecurityWebFilterChain Bean erstellt, die den Zugriff auf den Aktor, die zulässige URL und die URL, auf der das JWT-Token überprüft wird, konfiguriert. Es ist zu beachten, dass der Zugriff auf den JWT-Tokenfilter offen sein muss.


SpringWebFluxConfig.java-Konfiguration


In dieser Konfiguration werden MapReactiveUserDetailsService erstellt, um den Aktor und andere Systembenutzer im Speicher zu konfigurieren.


 @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); } 

Die ReactiveUserDetailsService die zum Zusammenfügen unseres Benutzerrepositorys mit Spring Security erforderlich ist.


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

Eine Bean zum Erstellen eines WebClient Clients zum Ausführen reaktiver Anforderungen.


 @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())); } 

Während der Erstellung werden zwei Filter hinzugefügt. LoadBalancer und der Filter, der die Authentication aus dem ReactiveSecurityContext Kontext entnimmt und daraus ein Token erstellt, sodass der Filter vom Zielserver authentifiziert und entsprechend autorisiert wird.


Und um die Arbeit mit dem MongoDB ObjectId Typ und den Daten zu ObjectId , habe ich einen objectMapper-Erstellungsbehälter hinzugefügt:


 @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(); } 

Microservice Spiel-Service


Der Microservice Game-Service hat folgende Struktur:


Alternativtext


Wie Sie darin sehen können, nur eine ApplicationConfig-Konfigurationsdatei


Konfigurator ApplicationConfig.java


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

Es enthält eine Variable mit der Adresse des user-service und es gibt zwei interessante Anmerkungen:


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

Diese Anmerkung ist erforderlich, um dem Konfigurator das MongoDB-Repository anzuzeigen.


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

Diese Annotation importiert die Konfigurationen aus dem auth-module .


GameService.java-Dienst


Dieser Dienst hat nur den folgenden interessanten Code:


 @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(); } 

Diese Methode löst zufällig eine Ausnahme aus und Hystrix gibt gemäß der Annotation das Ergebnis der folgenden Methode zurück:


 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); } 

Wie in dem oben erwähnten Buch angegeben, zeigen wir zwischengespeicherte oder alternative Daten besser als nichts, wenn etwas kaputt ist.


Microservice Webapi-Service


Dies ist eine Art Middleware zwischen dem Gateway und internen Microservices, die von außen nicht sichtbar sind. Der Zweck dieses Dienstes besteht darin, eine Auswahl aus anderen Diensten zu erhalten und dem Benutzer auf seiner Grundlage eine Antwort zu geben.


Alternativtext


Wir beginnen die Überprüfung mit der Konfiguration.


SpringSecurityWebFluxConfig.java-Konfiguration


 @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 ) { 

Hier erstellen wir einen Authentifizierungsmanager aus den userDetailsService Diensten, die wir zuvor im auth-module .


  UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService); 

Mit diesem Manager erstellen wir einen Filter und fügen einen Authentifizierungsinstanzkonverter hinzu, um Benutzerdaten in 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); }) ); 

Wir fügen einen erfolgreichen Autorisierungshandler hinzu, dessen Kern darin besteht, das JWT-Token in den aus der Authentication generierten Anforderungsheader zu setzen, sodass die Authentifizierung nur mit einem gültigen Gasttoken erfolgen kann.


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

Wir lösen Adressen aus der weißen Liste. Wie ich bereits geschrieben habe, müssen auch die Adressen geöffnet werden, die vom JWT-Filter verarbeitet werden


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

Wir schützen den Aktuator und einige Adressen mit Basisauthentifizierung


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

Der Zugriff auf Authentifizierungstoken ist obligatorisch


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

Filter hinzufügen. Authentifizierung und Überprüfung des JWT-Tokens.


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

Und wie ich oben geschrieben habe, deaktiviert dieser Dienst die für andere Dienste übliche JWT-Token-Prüfung, indem der Wert der Variablen micoservice=false in der Datei application.properites wird.


Token-Ausgabe-, Registrierungs- und Autorisierungscontroller AuthController.java


Ich werde diesen Controller nicht beschreiben, da er rein spezifisch ist.


WebApiService.java-Dienst


Dieser Dienst wird in der WebApiMethodProtectedController.jav als Controller bezeichnet und enthält eine interessante Anmerkung:


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

Diese Anmerkung ermöglicht den Zugriff auf Methoden nur autorisierten Benutzern mit der Gastrolle.


Wie zu testen


Erstellen Sie eine Umgebung:


Alternativtext


Holen Sie sich Token


Alternativtext


Aktualisieren Sie die Variable TOKEN in der Umgebung mit dem empfangenen Token.


Registrieren Sie einen neuen Benutzer


Alternativtext


Nach der Registrierung erhalten Sie ein Benutzertoken. Es läuft in 10 Stunden ab. Wenn es abläuft, müssen Sie ein neues bekommen. Fordern Sie dazu das Gast-Token erneut an, aktualisieren Sie die Umgebung und führen Sie die Anforderung aus


Alternativtext


Als Nächstes können Sie eine Liste der Benutzer und Spiele abrufen oder ein neues Spiel erstellen. Testen Sie auch Hystrix, siehe Servicekonfigurationen und Verschlüsselungsvariablen für das Git-Repository.


Referenzen


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


All Articles