рд╕реНрдкреНрд░рд┐рдВрдЧ рдХреНрд▓рд╛рдЙрдб рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдЯрд╛рд░реНрдЯрд░

рд╕рднреА рдХреЛ рдирдорд╕реНрдХрд╛рд░!


рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рд╕реНрдкреНрд░рд┐рдВрдЧ рд╡реЗрдмрдлреНрд▓рдХреНрд╕, рд╕реНрдкреНрд░рд┐рдВрдЧ рд╕рд┐рдХреНрдпреЛрд░рд┐рдЯреА, рд╕реНрдкреНрд░рд┐рдВрдЧ рдХреНрд▓рд╛рдЙрдб рдиреЗрдЯрдлреНрд▓рд┐рдХреНрд╕ рдпреВрд░реЗрдХрд╛ (рд╕рд░реНрд╡рд┐рд╕ рдбрд┐рд╕реНрдХрд╡рд░реА), рд╣рд┐рд╕реНрдЯреНрд░рд┐рдХреНрд╕ (рд╕рд░реНрдХрд┐рдЯ рдмреНрд░реЗрдХрд░), рд░рд┐рдмрди (рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рд╛рдЗрдб рд▓реЛрдб рдмреИрд▓реЗрдВрд╕рд░), рдмрд╛рд╣рд░реА рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди (рдЧрд┐рдЯ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд░рд┐рдПрдХреНрдЯрд┐рд╡ рд░рд┐рд╕реНрдЯрдлреБрд▓ рдорд┐рдХреНрд╕рд░ рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдмреБрдирд┐рдпрд╛рджреА рдШрдЯрдХреЛрдВ рдХрд╛ рдкреНрд░рджрд░реНрд╢рди рдХрд░реВрдВрдЧрд╛ред , рд╕реНрдкреНрд░рд┐рдВрдЧ рдХреНрд▓рд╛рдЙрдб рд╕реНрд▓реАрде, рд╕реНрдкреНрд░рд┐рдВрдЧ рдХреНрд▓рд╛рдЙрдб рдЧреЗрдЯрд╡реЗ, рд╕реНрдкреНрд░рд┐рдВрдЧ рдмреВрдЯ рд░рд┐рдПрдХреНрдЯрд┐рд╡ рдореЛрдВрдЧреЛрдмреАрдбреАред рд╕рд╛рде рд╣реА рдирд┐рдЧрд░рд╛рдиреА рдХреЗ рд▓рд┐рдП рд╕реНрдкреНрд░рд┐рдВрдЧ рдмреВрдЯ рдПрдбрдорд┐рди рдФрд░ рдЬрд┐рдкрдХрд┐рдиред


рдпрд╣ рд╕рдореАрдХреНрд╖рд╛ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХреНрд╢рди рдФрд░ рд╣реИрдВрдбреНрд╕-рдСрди рд╕реНрдкреНрд░рд┐рдВрдЧ 5 рд╕рд┐рдХреНрдпреЛрд░рд┐рдЯреА рдореЗрдВ рд╕реНрдкреНрд░рд┐рдВрдЧ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕реЗрд╕ рдХреА рдкреБрд╕реНрддрдХреЛрдВ рдХрд╛ рдЕрдзреНрдпрдпрди рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдХреА рдЧрдИ рдереАред


рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рд╣рдо рддреАрди рдкреНрд░рд╢реНрдиреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рдкреНрд░рд╛рдердорд┐рдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдмрдирд╛рдПрдВрдЧреЗ: рдЦреЗрд▓ рдХреА рдПрдХ рд╕реВрдЪреА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЦрд┐рд▓рд╛рдбрд╝рд┐рдпреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЦрд┐рд▓рд╛рдбрд╝рд┐рдпреЛрдВ рдХреА рдЖрдИрдбреА рд╕реЗ рдПрдХ рдЧреЗрдо рдмрдирд╛рдПрдВ, рдЬрд╡рд╛рдм рдХреЗ рд▓рд┐рдП рдПрдХ рд▓рдВрдмреЗ рдЗрдВрддрдЬрд╛рд░ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ рд░реЛрд▓рдмреИрдХ (рд╣рд┐рд╕реНрдЯреНрд░рд┐рдХреНрд╕ рдХрдордмреИрдХ) рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХрд╛ рдЕрдиреБрд░реЛрдзред рдФрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рд╢реАрд▓ рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдкреБрд╕реНрддрдХ рд╣реИрдВрдбреНрд╕-рдСрди рд╕реНрдкреНрд░рд┐рдВрдЧ 5 рд╕рд┐рдХреНрдпреЛрд░рд┐рдЯреА рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЬреЗрдбрдмреНрд▓реНрдпреВрдЯреА рдЯреЛрдХрди рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрдиред


рдореИрдВ рдпрд╣ рд╡рд░реНрдгрди рдирд╣реАрдВ рдХрд░реВрдВрдЧрд╛ рдХрд┐ рдЖрдИрдбреАрдИ рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд▓реЗрдЦ рдПрдХ рдЕрдиреБрднрд╡реА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рд▓рд┐рдП рд╣реИред


рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреА рд╕рдВрд░рдЪрдирд╛


Alt рдкрд╛рда


рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рджреЛ рдореЙрдбреНрдпреВрд▓ рд╢рд╛рдорд┐рд▓ рд╣реИрдВред spring-servers рдореЙрдбреНрдпреВрд▓ рдХреЛ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдореЗрдВ рд╕реБрд░рдХреНрд╖рд┐рдд рд░реВрдк рд╕реЗ рдХреЙрдкреА рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рд▓рдЧрднрдЧ рдХреЛрдИ рдХреЛрдб рдФрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдирд╣реАрдВ рд╣реИрдВред tictactoe-services рдореЙрдбреНрдпреВрд▓ рдореЗрдВ рд╣рдорд╛рд░реЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рдореЙрдбреНрдпреВрд▓ рдФрд░ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕ рд╢рд╛рдорд┐рд▓ рд╣реИрдВред рдореИрдВ рддреБрд░рдВрдд рдиреЛрдЯрд┐рд╕ рдХрд░реВрдВрдЧрд╛ рдХрд┐ рд╕реЗрд╡рд╛рдУрдВ рдореЗрдВ auth-module рдФрд░ domain-module рдХреЛ рдЬреЛрдбрд╝рддреЗ рд╣реБрдП, рдореИрдВ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдХрд╛ рдЙрд▓реНрд▓рдВрдШрди рдХрд░рддрд╛ рд╣реВрдВред рд▓реЗрдХрд┐рди рдЗрди рдореЙрдбреНрдпреВрд▓ рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреЗ рд╕реНрддрд░ рдкрд░, рдореЗрд░рд╛ рдорд╛рдирдирд╛ тАЛтАЛрд╣реИ рдХрд┐ рдпрд╣ рд╕рдмрд╕реЗ рдЗрд╖реНрдЯрддрдо рд╕рдорд╛рдзрд╛рди рд╣реИред


рдЧреНрд░реЗрдб рд╡рд┐рдиреНрдпрд╛рд╕


рдореБрдЭреЗ рдкрд╕рдВрдж рд╣реИ рдЬрдм рдкреВрд░рд╛ рдЧреНрд░реИрдбрд▓ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдПрдХ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рдкреВрд░реА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЛ рдПрдХ 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') } } } 

рдПрдХ рдХреЙрдорди рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╕реЗ рдЖрдк рдорд╛рдЗрдХреНрд░реЛрд╕реЗрд╡рд░реНрд╕ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрднрд░рддрд╛ рдХреЛ рд╕рд╛рдорд╛рдиреНрдп рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рд╕реЗрд╡рд╛рдУрдВ рдХреЛ "рд╕реЗрд╡рд╛" рдореЗрдВ рд╕рдорд╛рдкреНрдд рд╣реЛрдиреЗ рд╡рд╛рд▓реЗ рдирд╛рдо рдХреЗ рд╕рд╛рде, рдПрдХ рд╣реА рд╕реНрдерд╛рди рдкрд░ред рд▓реЗрдХрд┐рди, рдпрд╣ рдлрд┐рд░ рд╕реЗ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕ рдХреА рд╕реНрд╡рд╛рдпрддреНрддрддрд╛ рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрдд рдХрд╛ рдЙрд▓реНрд▓рдВрдШрди рдХрд░рддрд╛ рд╣реИред рд╕рд╛рдорд╛рдиреНрдп рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЖрдк рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдЙрдкрдкреНрд░реЛрдЬреЗрдХреНрдЯ рдореЗрдВ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред рдореИрдВрдиреЗ gradle.plugin.com.palantir.gradle.docker:gradle-docker рдЬреЛрдбрд╝ gradle.plugin.com.palantir.gradle.docker:gradle-docker рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП gradle.plugin.com.palantir.gradle.docker:gradle-docker рдкреНрд▓рдЧрдЗрди рдХрд╛рд░реНрдпред


рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдореЙрдбреНрдпреВрд▓ рдореЙрдбреНрдпреВрд▓


рдЕрдм, JWT рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдореЙрдбреНрдпреВрд▓ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВред рдЗрд╕ рдореЙрдбреНрдпреВрд▓ рдХреЗ auth рдкреИрдХреЗрдЬ рдХрд╛ рд╡рд┐рд╡рд░рдг рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдкреБрд╕реНрддрдХ рдореЗрдВ рдкрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬрд┐рд╕рдХрд╛ рдореИрдВрдиреЗ рдКрдкрд░ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рд╣реИред


Alt рдкрд╛рда


рдЖрд╣, config рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ config рдкреИрдХреЗрдЬ рдкрд░ рдзреНрдпрд╛рди config ред


"рдЬрдЯрд┐рд▓" рдЧреБрдгреЛрдВ рдХрд╛ рд╡рд░реНрдЧ 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; } } 

рдЗрд╕ рд╡рд░реНрдЧ рдореЗрдВ рдЗрдирдореЗрд░реА рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЗ рд▓рд┐рдП "рдЬрдЯрд┐рд▓" рдЧреБрдг рд╣реИрдВред


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

рд╕рдВрд╕рд╛рдзрди рдлрд╝рд╛рдЗрд▓ рдореЗрдВ, рдЖрдкрдХреЛ рдЗрди рдЪрд░реЛрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдореЗрд░реЗ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдореЗрдВ, рдЯреЛрдХрди 10 рдШрдВрдЯреЗ рдХреЗ рдмрд╛рдж рдореГрдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред


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

рдирд┐рд░реНрдорд╛рдг рдХреЗ рджреМрд░рд╛рди, рдпрд╣ рдлрд╝рд┐рд▓реНрдЯрд░ JWT рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реЗрд╡рд╛ рдХреЛ рдкрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЙрди рд░рд╛рд╕реНрддреЛрдВ рдХреА рд╕реВрдЪреА рд╣реИ рдЬреЛ рдпрд╣ рдлрд╝рд┐рд▓реНрдЯрд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд░реЗрдЧрд╛ред


рд░рд┐рдПрдХреНрдЯрд┐рд╡ рд╕реНрдкреНрд░рд┐рдВрдЧ рдмреВрдЯ рд╕рд┐рдХреНрдпреЛрд░рд┐рдЯреА рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕рд╕реНрдкрд╛рдпрд░рд┐рдВрдЧрд╕рд┐рдЯреАрд╡реЗрдмрдлреНрд▓рдХреНрд╕рдХреЙрдиреНрдлрд┐рдЧ.рдЬрд╡рд╛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреНрд▓рд╛рд╕


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

рдпрд╣рд╛рдВ рддреАрди рджрд┐рд▓рдЪрд╕реНрдк рдПрдиреЛрдЯреЗрд╢рди рд╣реИрдВред


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

рдПрдХ рдПрдиреЛрдЯреЗрд╢рди рдЬреЛ рдЗрд╕ рдореЙрдбреНрдпреВрд▓ рдХреЛ рдХрдиреЗрдХреНрдЯ рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд░ рдЪрд░ рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдПрдиреЛрдЯреЗрд╢рди рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рд╣реЛрддрд╛ рд╣реИред рдХреБрдЫ рдореЙрдбреНрдпреВрд▓ рдореЗрдВ рд╕рд╛рдорд╛рдиреНрдп рдЯреЛрдХрди рдЪреЗрдХрд┐рдВрдЧ рдХреЛ рдЕрдХреНрд╖рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдЗрд╕ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ, рдпрд╣ webapi-service , рдЬрд┐рд╕рдХрд╛ рд╕реНрд╡рдпрдВ рдХрд╛ SecurityWebFilterChain рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди webapi-service рдмреАрдиред


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

рдпрд╣ рдПрдиреЛрдЯреЗрд╢рди рдЖрдкрдХреЛ рдореБрдЦреНрдп рд╕реЗрд╡рд╛ рд╕реЗ рдЧреБрдг рд▓реЗрдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдпрд╣ рдореЙрдбреНрдпреВрд▓ рдЖрдпрд╛рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рджреВрд╕рд░реЗ рд╢рдмреНрджреЛрдВ рдореЗрдВ, рдЪрд░


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

рдЙрдирдХреЗ рдореВрд▓реНрдпреЛрдВ рдХреЛ рд╡рдВрд╢ рдХреЗ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рд╕реЗ рд▓реЗрдВред


рдФрд░, рдПрдХ рдПрдиреЛрдЯреЗрд╢рди рдЬреЛ рдЖрдкрдХреЛ @PreAuthorize(тАЬhasRole('MY_ROLE')тАЭ) рдЬреИрд╕реЗ рд╕реБрд░рдХреНрд╖рд╛ рдПрдиреЛрдЯреЗрд╢рди рдХреЛ рд▓рдЯрдХрд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ


 @EnableReactiveMethodSecurity 

рдФрд░ рдЗрд╕ рдореЙрдбреНрдпреВрд▓ рдореЗрдВ, SecurityWebFilterChain рдмреАрди рдмрдирд╛рдИ рдЬрд╛рддреА рд╣реИ, рдЬреЛ рдПрдХреНрдЯреНрдпреВрдПрдЯрд░ рддрдХ рдкрд╣реБрдБрдЪ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рддреА рд╣реИ, рдЕрдиреБрдорддрд┐ рджреА рдЧрдИ url рдФрд░ url рдЬрд┐рд╕ рдкрд░ JWT рдЯреЛрдХрди рдЪреЗрдХ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдпрд╣ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ JWT рдЯреЛрдХрди рдлрд╝рд┐рд▓реНрдЯрд░ рддрдХ рдкрд╣реБрдВрдЪ рдЖрд╡рд╢реНрдпрдХ рд╣реИред


SpringWebFluxConfig.java рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди


рдЗрд╕ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдореЗрдВ, MapReactiveUserDetailsService рдХреЛ рдПрдХреНрдЯреНрдпреВрдПрдЯрд░ рдФрд░ рдЕрдиреНрдп рд╕рд┐рд╕реНрдЯрдо рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рд╕реНрдореГрддрд┐ рдореЗрдВ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред


 @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 рдЬреЛ Spring Security рд╕рд╛рде рд╣рдорд╛рд░реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рднрдВрдбрд╛рд░ рдХреЛ рд╕рд┐рд▓рд╛рдИ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИред


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

рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рд╢реАрд▓ рдЕрдиреБрд░реЛрдз рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ WebClient рдХреНрд▓рд╛рдЗрдВрдЯ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдмреАрдиред


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

рдирд┐рд░реНрдорд╛рдг рдХреЗ рджреМрд░рд╛рди, рджреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдЬреЛрдбрд╝реЗ рдЬрд╛рддреЗ рд╣реИрдВред LoadBalancer рдФрд░ рдПрдХ рдлрд╝рд┐рд▓реНрдЯрд░ рдЬреЛ ReactiveSecurityContext рд╕рдВрджрд░реНрдн рд╕реЗ рдПрдХ Authentication рдЙрджрд╛рд╣рд░рдг рд▓реЗрддрд╛ рд╣реИ рдФрд░ рдЗрд╕рд╕реЗ рдПрдХ рдЯреЛрдХрди рдмрдирд╛рддрд╛ рд╣реИ рддрд╛рдХрд┐ рд▓рдХреНрд╖реНрдп рд╕рд░реНрд╡рд░ рдЗрд╕реЗ рдкреНрд░рдорд╛рдгрд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рддрджрдиреБрд╕рд╛рд░ рдЕрдзрд┐рдХреГрдд рдХрд░рддрд╛ рд╣реИред


рдФрд░ MongoDB ObjectId рдкреНрд░рдХрд╛рд░ рдФрд░ рджрд┐рдирд╛рдВрдХ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рдПрдХ рдСрдмреНрдЬреЗрдХреНрдЯрдореИрдкрд░ рдирд┐рд░реНрдорд╛рдг рдмрд┐рди рдЬреЛрдбрд╝рд╛:


 @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 рдЦреЗрд▓-рд╕реЗрд╡рд╛


Microservice рдЦреЗрд▓-рд╕реЗрд╡рд╛ рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕рдВрд░рдЪрдирд╛ рд╣реИ:


Alt рдкрд╛рда


рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдЗрд╕рдореЗрдВ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдХреЗрд╡рд▓ рдПрдХ ApplicationConfig рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓


рд╡рд┐рдиреНрдпрд╛рд╕рдХ ApplicationConfig.java


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

рдЗрд╕рдореЗрдВ user-service рдХреЗ рдкрддреЗ рдХреЗ рд╕рд╛рде рдПрдХ рдЪрд░ рд╣реИ рдФрд░ рджреЛ рджрд┐рд▓рдЪрд╕реНрдк рдПрдиреЛрдЯреЗрд╢рди рд╣реИрдВ:


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

рдЗрд╕ рдПрдиреЛрдЯреЗрд╢рди рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рдирдХрд░реНрддрд╛ рдХреЛ MongoDB рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред


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

рдпрд╣ рдПрдиреЛрдЯреЗрд╢рди auth-module рд╕реЗ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдЖрдпрд╛рдд рдХрд░рддрд╛ рд╣реИред


GameService.java рд╕реЗрд╡рд╛


рдЗрд╕ рд╕реЗрд╡рд╛ рдореЗрдВ рдХреЗрд╡рд▓ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рджрд┐рд▓рдЪрд╕реНрдк рдХреЛрдб рд╣реИрдВ:


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

рдпрд╣ рд╡рд┐рдзрд┐ рдмреЗрддрд░рддреАрдм рдврдВрдЧ рд╕реЗ рдПрдХ рдЕрдкрд╡рд╛рдж рдлреЗрдВрдХрддрд╛ рд╣реИ рдФрд░ рдПрдирд╕реНрдЯреНрд░реЗрд╢рди рдХреЗ рдЕрдиреБрд╕рд╛рд░ рд╣рд┐рд╕реНрдЯреНрд░рд┐рдХреНрд╕ рдирд┐рдореНрди рд╡рд┐рдзрд┐ рдХрд╛ рдкрд░рд┐рдгрд╛рдо рджреЗрддрд╛ рд╣реИ:


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

рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рдЙрд▓реНрд▓рд┐рдЦрд┐рдд рдкреБрд╕реНрддрдХ рдореЗрдВ рдХрд╣рд╛ рдЧрдпрд╛ рд╣реИ, рдЕрдЧрд░ рдХреБрдЫ рдЯреВрдЯрд╛ рд╣реБрдЖ рд╣реИ, рддреЛ рдЪрд▓реЛ рдХреИрд╢реНрдб рдпрд╛ рд╡реИрдХрд▓реНрдкрд┐рдХ рдбреЗрдЯрд╛ рдХреЛ рдХреБрдЫ рдирд╣реАрдВ рд╕реЗ рдмреЗрд╣рддрд░ рджрд┐рдЦрд╛рддреЗ рд╣реИрдВред


рдорд╛рдЗрдХреНрд░реЛ рд╕рд░реНрд╡рд┐рд╕ рд╡реЗрдмрдкрдИ-рд╕реЗрд╡рд╛


рдпрд╣ рдЧреЗрдЯрд╡реЗ рдФрд░ рдЖрдВрддрд░рд┐рдХ microservices рдХреЗ рдмреАрдЪ рдХрд╛ рдПрдХ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдорд┐рдбрд▓рд╡реЗрдпрд░ рд╣реИ рдЬреЛ рдмрд╛рд╣рд░ рд╕реЗ рджрд┐рдЦрд╛рдИ рдирд╣реАрдВ рджреЗрддрд╛ рд╣реИред рдЗрд╕ рд╕реЗрд╡рд╛ рдХрд╛ рдЙрджреНрджреЗрд╢реНрдп рдЕрдиреНрдп рд╕реЗрд╡рд╛рдУрдВ рд╕реЗ рдЪрдпрди рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдФрд░ рдЗрд╕рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рддреИрдпрд╛рд░ рдХрд░рдирд╛ рд╣реИред


Alt рдкрд╛рда


рд╣рдо рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЗ рд╕рд╛рде рд╕рдореАрдХреНрд╖рд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред


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

рдпрд╣рд╛рдВ рд╣рдо userDetailsService рд╕реЗрд╡рд╛рдУрдВ рд╕реЗ рдПрдХ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдкреНрд░рдмрдВрдзрдХ userDetailsService рд╣реИрдВ, рдЬрд┐рд╕реЗ рд╣рдордиреЗ рдкрд╣рд▓реЗ userDetailsService auth-module рдореЗрдВ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд┐рдпрд╛ рдерд╛ред


  UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService); 

рдФрд░ рд╣рдо рдЗрд╕ рдкреНрд░рдмрдВрдзрдХ рдХреЗ рд╕рд╛рде рдПрдХ рдлрд╝рд┐рд▓реНрдЯрд░ рдмрдирд╛рддреЗ рд╣реИрдВ, рдФрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛-рдбреЗрдЯрд╛ рдХреЛ 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); }) ); 

рд╣рдо рдПрдХ рд╕рдлрд▓ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рд╣реИрдВрдбрд▓рд░ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдХрд╛ рд╕рд╛рд░ Authentication рд╕реЗ рдЙрддреНрдкрдиреНрди рдЕрдиреБрд░реЛрдз рд╣реЗрдбрд░ рдореЗрдВ JWT рдЯреЛрдХрди рдбрд╛рд▓рдирд╛ рд╣реИ рддрд╛рдХрд┐ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗрд╡рд▓ рдПрдХ рдорд╛рдиреНрдп рдЕрддрд┐рдерд┐ рдЯреЛрдХрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХреЗред


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

рд╣рдо рд╢реНрд╡реЗрдд рд╕реВрдЪреА рд╕реЗ рдкрддреЗ рд╣рд▓ рдХрд░рддреЗ рд╣реИрдВред рдЬреИрд╕рд╛ рдХрд┐ рдореИрдВрдиреЗ рдкрд╣рд▓реЗ рд▓рд┐рдЦрд╛ рдерд╛, JWT рдлрд╝рд┐рд▓реНрдЯрд░ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдкрддреЗ рднреА рдЦреЛрд▓реЗ рдЬрд╛рдиреЗ рдЪрд╛рд╣рд┐рдП


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

рд╣рдо рдореВрд▓ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд╕рд╛рде рдПрдХреНрдЯреНрдпреВрдПрдЯрд░ рдФрд░ рдХреБрдЫ рдкрддреЗ рдХреА рд░рдХреНрд╖рд╛ рдХрд░рддреЗ рд╣реИрдВ


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

рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдЯреЛрдХрди рдкрд╣реБрдВрдЪ рдЕрдирд┐рд╡рд╛рд░реНрдп рдХрд░рдирд╛


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

рдлрд╝рд┐рд▓реНрдЯрд░ рдЬреЛрдбрд╝реЗрдВред JWT рдЯреЛрдХрди рдХреЛ рдкреНрд░рдорд╛рдгрд┐рдд рдФрд░ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред


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

рдФрд░ рдЬреИрд╕рд╛ рдХрд┐ рдореИрдВрдиреЗ рдКрдкрд░ рд▓рд┐рдЦрд╛ рд╣реИ, рдпрд╣ рд╕реЗрд╡рд╛ JWT рдЯреЛрдХрди рдЪреЗрдХ, рдЕрдиреНрдп рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рд▓рд┐рдП рд╕рд╛рдорд╛рдиреНрдп, application.properites рдореЗрдВ micoservice=false рдЪрд░ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдХреЗ рдирд┐рд╖реНрдХреНрд░рд┐рдп рдХрд░ рджреЗрддреА рд╣реИред


рдЯреЛрдХрди рдЬрд╛рд░реА рдХрд░рдирд╛, рдкрдВрдЬреАрдХрд░рдг рдФрд░ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдирд┐рдпрдВрддреНрд░рдХ AuthController.java


рдореИрдВ рдЗрд╕ рдирд┐рдпрдВрддреНрд░рдХ рдХрд╛ рд╡рд░реНрдгрди рдирд╣реАрдВ рдХрд░реВрдВрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд╡рд┐рд╢реБрджреНрдз рд░реВрдк рд╕реЗ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╣реИред


WebApiService.java рд╕реЗрд╡рд╛


рдЗрд╕ рд╕реЗрд╡рд╛ рдХреЛ WebApiMethodProtectedController.jav рдПрдХ рдирд┐рдпрдВрддреНрд░рдХ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдПрдХ рджрд┐рд▓рдЪрд╕реНрдк рдЯрд┐рдкреНрдкрдгреА рд╣реИ:


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

рдпрд╣ рдПрдиреЛрдЯреЗрд╢рди рдХреЗрд╡рд▓ рдЕрддрд┐рдерд┐ рднреВрдорд┐рдХрд╛ рд╡рд╛рд▓реЗ рдЕрдзрд┐рдХреГрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рддрд░реАрдХреЛрдВ рддрдХ рдкрд╣реБрдВрдЪ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред


рдкрд░реАрдХреНрд╖рд╛ рдХреИрд╕реЗ рджреЗрдВ


рдПрдХ рд╡рд╛рддрд╛рд╡рд░рдг рдмрдирд╛рдПрдБ:


Alt рдкрд╛рда


рдЯреЛрдХрди рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ


Alt рдкрд╛рда


рдкреНрд░рд╛рдкреНрдд рдЯреЛрдХрди рдХреЗ рд╕рд╛рде рдкрд░реНрдпрд╛рд╡рд░рдг рдореЗрдВ TOKEN рдЪрд░ рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВред


рдПрдХ рдирдпрд╛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкрдВрдЬреАрдХреГрдд рдХрд░реЗрдВ


Alt рдкрд╛рда


рдкрдВрдЬреАрдХрд░рдг рдХреЗ рдмрд╛рдж, рдЖрдкрдХреЛ рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЯреЛрдХрди рдкреНрд░рд╛рдкреНрдд рд╣реЛрдЧрд╛ред рдпрд╣ 10 рдШрдВрдЯреЗ рдореЗрдВ рд╕рдорд╛рдкреНрдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред рдЬрдм рдпрд╣ рд╕рдорд╛рдкреНрдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рддреЛ рдЖрдкрдХреЛ рдПрдХ рдирдпрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЕрддрд┐рдерд┐ рдЯреЛрдХрди рдХреЛ рдлрд┐рд░ рд╕реЗ рдЕрдиреБрд░реЛрдз рдХрд░реЗрдВ, рдкрд░реНрдпрд╛рд╡рд░рдг рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВ рдФрд░ рдЕрдиреБрд░реЛрдз рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░реЗрдВ


Alt рдкрд╛рда


рдЗрд╕рдХреЗ рдмрд╛рдж, рдЖрдк рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреА рд╕реВрдЪреА, рдЧреЗрдо рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╛ рдПрдХ рдирдпрд╛ рдЧреЗрдо рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВред рдФрд░ рд╣рд┐рд╕реНрдЯреНрд░рд┐рдХреНрд╕ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рднреА рдХрд░рддреЗ рд╣реИрдВ, git рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдХреЗ рд▓рд┐рдП рд╕реЗрд╡рд╛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдФрд░ рдПрдиреНрдХреНрд░рд┐рдкреНрдЯ рд╡реИрд░рд┐рдПрдмрд▓ рджреЗрдЦреЗрдВред


рд╕рдВрджрд░реНрдн


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


All Articles