Spring Cloud - Sikring af tjenester

1. Oversigt

I den forrige artikel, Spring Cloud - Bootstrapping, har vi bygget en grundlæggende Spring Cloud Ansøgning. Denne artikel viser, hvordan du sikrer det.

Vi bruger naturligvis Forårssikkerhed at dele sessioner ved hjælp af Forårsession og Redis. Denne metode er enkel at konfigurere og let at udvide til mange forretningsscenarier. Hvis du ikke er bekendt med Forårsession, tjek denne artikel.

Delsessioner giver os muligheden for at logge brugere i vores gateway-tjeneste og udbrede denne godkendelse til enhver anden service i vores system.

Hvis du ikke er bekendt med Redis ellerForårssikkerhed, er det en god idé at foretage en hurtig gennemgang af disse emner på dette tidspunkt. Mens meget af artiklen er kopipasta klar til en applikation, er der ingen erstatning for at forstå, hvad der sker under emhætten.

For en introduktion til Redis læs denne vejledning. For en introduktion til Forårssikkerhed læs forår-sikkerhed-login, rolle-og-privilegium-for-forår-sikkerhed-registrering og forår-sikkerhed-session. For at få en fuldstændig forståelse af Forårssikkerhed, se på lær-fjeder-sikkerhed-mesterklassen.

2. Maven-opsætning

Lad os starte med at tilføje fjeder-boot-starter-sikkerhedsafhængighed til hvert modul i systemet:

 org.springframework.boot spring-boot-starter-security 

Fordi vi bruger Forår afhængighedsstyring, vi kan udelade versionerne til spring-boot-starter afhængigheder.

Lad os som et andet trin ændre pom.xml af hver applikation med spring-session, spring-boot-starter-data-redis afhængigheder:

 org.springframework.session forårssession org.springframework.boot spring-boot-starter-data-redis 

Kun fire af vores ansøgninger hænger sammen Forårsession: opdagelse, gateway, bog-serviceog rating-service.

Dernæst tilføj en sessionskonfigurationsklasse i alle tre tjenester i samme bibliotek som hovedapplikationsfilen:

@EnableRedisHttpSession offentlig klasse SessionConfig udvider AbstractHttpSessionApplicationInitializer {}

Til sidst skal du tilføje disse egenskaber til de tre *.ejendomme filer i vores git-arkiv:

spring.redis.host = localhost spring.redis.port = 6379

Lad os nu hoppe ind i tjenestespecifik konfiguration.

3. Sikring af konfigurationsservice

Konfigurationstjenesten indeholder følsomme oplysninger, der ofte er relateret til databaseforbindelser og API-nøgler. Vi kan ikke kompromittere disse oplysninger, så lad os dykke lige ind og sikre denne service.

Lad os tilføje sikkerhedsegenskaber til application.properties fil i src / main / ressourcer af konfigurationstjenesten:

eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka / security.user.name = configUser security.user.password = configPassword security.user.role = SYSTEM

Dette vil oprette vores service til login med opdagelse. Derudover konfigurerer vi vores sikkerhed med application.properties fil.

Lad os nu konfigurere vores opdagelsestjeneste.

4. Sikring af opdagelsestjeneste

Vores opdagelsestjeneste indeholder følsomme oplysninger om placeringen af ​​alle tjenesterne i applikationen. Det registrerer også nye forekomster af disse tjenester.

Hvis ondsindede klienter får adgang, vil de lære netværksplacering af alle tjenester i vores system og være i stand til at registrere deres egne ondsindede tjenester i vores applikation. Det er afgørende, at opdagelsestjenesten er sikret.

4.1. Sikkerhedskonfiguration

Lad os tilføje et sikkerhedsfilter for at beskytte de slutpunkter, de andre tjenester bruger:

@Configuration @EnableWebSecurity @Order (1) offentlig klasse SecurityConfig udvider WebSecurityConfigurerAdapter {@Autowired public void configureGlobal (AuthenticationManagerBuilder auth) {auth.inMemoryAuthentication (). WithUser ("discUser") .password (")" password. " ); } @ Override beskyttet ugyldig konfiguration (HttpSecurity http) {http.sessionManagement () .sessionCreationPolicy (SessionCreationPolicy.ALWAYS) .and (). RequestMatchers (). AntMatchers ("/ eureka / **"). Og (). AuthorizeRequests () .antMatchers ("/ eureka / **") .hasRole ("SYSTEM"). anyRequest (). denyAll (). og () .httpBasic (). og (). csrf (). deaktiver (); }}

Dette vil oprette vores service med en 'SYSTEM'Bruger. Dette er en grundlæggende Forårssikkerhed konfiguration med et par vendinger. Lad os se på disse vendinger:

  • @Bestilling (1) - fortæller Forår at koble dette sikkerhedsfilter først, så det forsøges før andre
  • .sessionCreationPolicy - fortæller Forår for altid at oprette en session, når en bruger logger på dette filter
  • .requestMatchers - begrænser hvilke slutpunkter dette filter gælder for

Sikkerhedsfilteret, som vi lige har oprettet, konfigurerer et isoleret godkendelsesmiljø, der kun vedrører opdagelsestjenesten.

4.2. Sikring af Eureka Dashboard

Da vores opdagelsesapplikation har en god brugergrænseflade til at se aktuelt registrerede tjenester, lad os udsætte det ved hjælp af et andet sikkerhedsfilter og binde dette til godkendelsen for resten af ​​vores applikation. Husk, at nej @Bestille() tag betyder, at dette er det sidste sikkerhedsfilter, der evalueres:

@Configuration offentlig statisk klasse AdminSecurityConfig udvider WebSecurityConfigurerAdapter {@Override beskyttet ugyldig konfiguration (HttpSecurity http) {http.sessionManagement (). SessionCreationPolicy (SessionCreationPolicy.NEVER). Og (). HttpBasic (). Disable (). Author. HttpMethod.GET, "/").hasRole("ADMIN") .antMatchers ("/ info", "/ health"). Godkendt (). AnyRequest () .denyAll (). Og (). Csrf (). Deaktiver (); }}

Tilføj denne konfigurationsklasse inden for Sikkerhedskonfig klasse. Dette opretter et andet sikkerhedsfilter, der styrer adgangen til vores brugergrænseflade. Dette filter har et par usædvanlige egenskaber, lad os se på dem:

  • httpBasic (). deaktiver () - fortæller forårssikkerhed at deaktivere alle godkendelsesprocedurer for dette filter
  • sessionCreationPolicy - vi satte dette til ALDRIG for at angive, at vi kræver, at brugeren allerede er godkendt inden adgang til ressourcer, der er beskyttet af dette filter

Dette filter indstiller aldrig en brugersession og er afhængig af Redis at udfylde en delt sikkerhedskontekst. Som sådan er det afhængigt af en anden tjeneste, gatewayen, for at levere godkendelse.

4.3. Godkendelse med Config Service

Lad os i opdagelsesprojektet føje to egenskaber til bootstrap.properties i src / main / ressourcer:

spring.cloud.config.username = config Bruger spring.cloud.config.password = configPassword

Disse egenskaber lader opdagelsestjenesten godkendes med konfigurationstjenesten ved opstart.

Lad os opdatere vores discovery.properties i vores Git-arkiv

eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka / eureka.client.register-with-eureka = false eureka.client.fetch-registry = false

Vi har tilføjet grundlæggende godkendelsesoplysninger til vores opdagelse tjeneste, så den kan kommunikere med config service. Derudover konfigurerer vi Eureka at køre i standalone-tilstand ved at fortælle vores service ikke at registrere sig selv.

Lad os forpligte filen til git lager. Ellers registreres ændringerne ikke.

5. Sikring af gateway-service

Vores gateway-service er det eneste stykke af vores applikation, vi vil udsætte for verden. Som sådan har det brug for sikkerhed for at sikre, at kun godkendte brugere har adgang til følsomme oplysninger.

5.1. Sikkerhedskonfiguration

Lad os oprette en Sikkerhedskonfig klasse som vores opdagelsestjeneste og overskriv metoderne med dette indhold:

@Autowired public void configureGlobal (AuthenticationManagerBuilder auth) {auth.inMemoryAuthentication (). WithUser ("user"). Password ("password") .roles ("USER"). And (). WithUser ("admin"). Password ( "admin") .roles ("ADMIN"); } @ Override beskyttet ugyldig konfiguration (HttpSecurity http) {http.authorizeRequests (). AntMatchers ("/ book-service / books") .permitAll (). AntMatchers ("/ eureka / **"). HasRole ("ADMIN") .anyRequest (). godkendt (). og (). formLogin (). og () .logout (). permitAll (). logoutSuccessUrl ("/ bogtjeneste / bøger") .permitAll (). og (). csrf () .disable (); }

Denne konfiguration er ret ligetil. Vi erklærer et sikkerhedsfilter med formularlogin, der sikrer en række slutpunkter.

Sikkerheden på / eureka / ** er at beskytte nogle statiske ressourcer, vi vil betjene fra vores gatewaytjeneste til Eureka statusside. Hvis du bygger projektet med artiklen, skal du kopiere ressource / statisk mappe fra gateway-projektet på Github til dit projekt.

Nu ændrer vi @EnableRedisHttpSession kommentar på vores konfigurationsklasse:

@EnableRedisHttpSession (redisFlushMode = RedisFlushMode.IMMEDIATE)

Vi indstiller skylletilstanden til øjeblikkelig for at fortsætte eventuelle ændringer på sessionen med det samme. Dette hjælper med at forberede godkendelsestokenet til omdirigering.

Lad os endelig tilføje en ZuulFilter der videresender vores godkendelsestoken efter login:

@Komponent offentlig klasse SessionSavingZuulPreFilter udvider ZuulFilter {@Autowired private SessionRepository repository; @Override public boolean shouldFilter () {return true; } @Override public Object run () {RequestContext context = RequestContext.getCurrentContext (); HttpSession httpSession = context.getRequest (). GetSession (); Sessionssession = repository.getSession (httpSession.getId ()); context.addZuulRequestHeader ("Cookie", "SESSION =" + httpSession.getId ()); returnere null; } @ Override public String filterType () {return "pre"; } @Override public int filterOrder () {return 0; }}

Dette filter griber anmodningen, da den omdirigeres efter login og tilføjer sessionstasten som en cookie i overskriften. Dette vil udbrede godkendelse til enhver backingtjeneste efter login.

5.2. Godkendelse med konfigurations- og opdagelsesservice

Lad os tilføje følgende godkendelsesegenskaber til bootstrap.properties fil i src / main / ressourcer af gatewaytjenesten:

spring.cloud.config.username = configUser spring.cloud.config.password = configPassword eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka /

Lad os derefter opdatere vores gateway.properties i vores Git-arkiv

management.security.sessions = altid zuul.routes.book-service.path = / book-service / ** zuul.routes.book-service.sensitive-headers = Set-cookie, autorisation hystrix.command.book-service.execution .isolation.thread .timeoutInMilliseconds = 600000 zuul.routes.rating-service.path = / rating-service / ** zuul.routes.rating-service.sensitive-headers = Set-Cookie, Authorization hystrix.command.rating-service. udførelse.isolation.thread .timeoutInMilliseconds = 600000 zuul.routes.discovery.path = / discovery / ** zuul.routes.discovery.sensitive-headers = Set-Cookie, autorisation zuul.routes.discovery.url = // localhost: 8082 hystrix.command.discovery.execution.isolation.thread .timeoutInMilliseconds = 600000

Vi har tilføjet sessionsstyring for altid at generere sessioner, fordi vi kun har et sikkerhedsfilter, som vi kan indstille i egenskabsfilen. Dernæst tilføjer vi vores Redis vært- og serveregenskaber.

Derudover tilføjede vi en rute, der omdirigerer anmodninger til vores opdagelsestjeneste. Da en uafhængig opdagelsestjeneste ikke registreres hos sig selv, skal vi lokalisere den service med et URL-skema.

Vi kan fjerne serviceUrl.defaultZone ejendom fra gateway.properties fil i vores konfigurationsgit-arkiv. Denne værdi duplikeres i bootstrap fil.

Lad os forpligte filen til Git-arkivet, ellers opdages ændringerne ikke.

6. Sikring af bogservice

Bogserviceserveren indeholder følsomme oplysninger, der styres af forskellige brugere. Denne service skal sikres for at forhindre lækager af beskyttede oplysninger i vores system.

6.1. Sikkerhedskonfiguration

For at sikre vores bogtjeneste kopierer vi Sikkerhedskonfig klasse fra gatewayen og overskrive metoden med dette indhold:

@ Override beskyttet ugyldig konfiguration (HttpSecurity http) {http.httpBasic (). Deaktiver (). AuthorizeRequests () .antMatchers ("/ books"). PermitAll () .antMatchers ("/ books / *"). HasAnyRole ("USER "," ADMIN "). Godkendt (). Og (). Csrf (). Deaktiver (); }

6.2. Ejendomme

Føj disse egenskaber til bootstrap.properties fil i src / main / ressourcer af bogtjenesten:

spring.cloud.config.username = configUser spring.cloud.config.password = configPassword eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka /

Lad os tilføje egenskaber til vores book-service.properties fil i vores git-arkiv:

management.security.sessions = aldrig

Vi kan fjerne serviceUrl.defaultZone ejendom fra book-service.properties fil i vores konfigurationsgit-arkiv. Denne værdi duplikeres i bootstrap fil.

Husk at foretage disse ændringer, så bogtjenesten henter dem.

7. Sikring af vurderingstjeneste

Ratingtjenesten skal også sikres.

7.1. Sikkerhedskonfiguration

For at sikre vores vurderingstjeneste kopierer vi Sikkerhedskonfig klasse fra gatewayen og overskrive metoden med dette indhold:

@ Override beskyttet ugyldig konfiguration (HttpSecurity http) {http.httpBasic (). Deaktiver (). AuthorizeRequests () .antMatchers ("/ ratings"). HasRole ("USER") .antMatchers ("/ ratings / all"). HasAnyRole ("BRUGER", "ADMIN"). AnyRequest (). Godkendt (). Og (). Csrf (). Deaktiver (); }

Vi kan slette configureGlobal () metode fra gateway service.

7.2. Ejendomme

Føj disse egenskaber til bootstrap.properties fil i src / main / ressourcer af vurderingstjenesten:

spring.cloud.config.username = configUser spring.cloud.config.password = configPassword eureka.client.serviceUrl.defaultZone = // discUser: [email protected]: 8082 / eureka /

Lad os tilføje egenskaber til vores vurderingstjeneste.ejendomme fil i vores git-arkiv:

management.security.sessions = aldrig

Vi kan fjerne serviceUrl.defaultZone ejendom fra vurderingstjenesten.ejendomme fil i vores konfigurationsgit-arkiv. Denne værdi duplikeres i bootstrap fil.

Husk at foretage disse ændringer, så vurderingstjenesten henter dem.

8. Kørsel og test

Start Redis og alle tjenester til applikationen: config, opdagelse, gateway, book-service, og rating-service. Lad os nu teste!

Lad os først oprette en testklasse i vores gateway projekter og opret en metode til vores test:

offentlig klasse GatewayApplicationLiveTest {@Test offentlig ugyldighed testAccess () {...}}

Lad os derefter oprette vores test og validere, at vi kan få adgang til vores ubeskyttede / bog-service / bøger ressource ved at tilføje dette kodestykke i vores testmetode:

TestRestTemplate testRestTemplate = ny TestRestTemplate (); Streng testUrl = "// localhost: 8080"; ResponseEntity response = testRestTemplate .getForEntity (testUrl + "/ book-service / books", String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ()); Assert.assertNotNull (respons.getBody ());

Kør denne test og kontroller resultaterne. Hvis vi ser fejl, skal du bekræfte, at hele applikationen startede med succes, og at konfigurationer blev indlæst fra vores konfigurationsgit-arkiv.

Lad os nu teste, at vores brugere vil blive omdirigeret til at logge ind, når de besøger en beskyttet ressource som en uautoriseret bruger ved at tilføje denne kode til slutningen af ​​testmetoden:

respons = testRestTemplate .getForEntity (testUrl + "/ bogtjeneste / bøger / 1", String.class); Assert.assertEquals (HttpStatus.FOUND, response.getStatusCode ()); Assert.assertEquals ("// localhost: 8080 / login", response.getHeaders () .get ("Location"). Get (0));

Kør testen igen, og bekræft, at den lykkes.

Lad os derefter logge ind og derefter bruge vores session til at få adgang til det brugerbeskyttede resultat:

MultiValueMap form = ny LinkedMultiValueMap (); form.add ("brugernavn", "bruger"); form.add ("adgangskode", "adgangskode"); respons = testRestTemplate .postForEntity (testUrl + "/ login", form, String.class); 

lad os nu udtrække sessionen fra cookien og udbrede den til følgende anmodning:

Streng sessionCookie = respons.getHeaders (). Get ("Set-Cookie") .get (0) .split (";") [0]; HttpHeaders headers = nye HttpHeaders (); headers.add ("Cookie", sessionCookie); HttpEntity httpEntity = ny HttpEntity (overskrifter); 

og anmode om den beskyttede ressource:

respons = testRestTemplate.exchange (testUrl + "/ bogtjeneste / bøger / 1", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ()); Assert.assertNotNull (respons.getBody ());

Kør testen igen for at bekræfte resultaterne.

Lad os nu prøve at få adgang til admin-sektionen med den samme session:

respons = testRestTemplate.exchange (testUrl + "/ rating-service / ratings / all", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.FORBIDDEN, respons.getStatusCode ());

Kør testen igen, og som forventet er vi begrænset fra at få adgang til adminområder som en almindelig gammel bruger.

Den næste test validerer, at vi kan logge ind som administrator og få adgang til den adminbeskyttede ressource:

form.clear (); form.add ("brugernavn", "admin"); form.add ("adgangskode", "admin"); respons = testRestTemplate .postForEntity (testUrl + "/ login", form, String.class); sessionCookie = respons.getHeaders (). get ("Set-Cookie"). get (0) .split (";") [0]; headers = nye HttpHeaders (); headers.add ("Cookie", sessionCookie); httpEntity = ny HttpEntity (overskrifter); respons = testRestTemplate.exchange (testUrl + "/ rating-service / ratings / all", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ()); Assert.assertNotNull (respons.getBody ());

Vores test bliver stor! Men vi kan se, når vi kører det, at ved at logge ind som administrator får vi adgang til admin-ressourcen.

Vores sidste test er at få adgang til vores opdagelsesserver gennem vores gateway. For at gøre dette skal du tilføje denne kode i slutningen af ​​vores test:

respons = testRestTemplate.exchange (testUrl + "/ discovery", HttpMethod.GET, httpEntity, String.class); Assert.assertEquals (HttpStatus.OK, response.getStatusCode ());

Kør denne test en sidste gang for at bekræfte, at alt fungerer. Succes!!!

Savnede du det? Fordi vi loggede ind på vores gatewaytjeneste og så indhold på vores bog-, rating- og opdagelsestjenester uden at skulle logge på fire separate servere!

Ved at bruge Forårsession for at udbrede vores godkendelsesobjekt mellem servere er vi i stand til at logge ind en gang på gatewayen og bruge denne godkendelse til at få adgang til controllere på et hvilket som helst antal backingtjenester.

9. Konklusion

Sikkerhed i skyen bliver bestemt mere kompliceret. Men ved hjælp af Forårssikkerhed og Forårsession, kan vi nemt løse dette kritiske problem.

Vi har nu en skyapplikation med sikkerhed omkring vores tjenester. Ved brug af Zuul og Forårsession Vi kan kun logge brugere i en tjeneste og udbrede denne godkendelse til hele vores applikation. Dette betyder, at vi let kan opdele vores ansøgning i korrekte domæner og sikre hver af dem, som vi finder det passende.

Som altid kan du finde kildekoden på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found