Spring Security 5 til reaktive applikationer

1. Introduktion

I denne artikel udforsker vi nye funktioner i Spring Security 5-rammen til sikring af reaktive applikationer. Denne udgivelse er tilpasset Spring 5 og Spring Boot 2.

I denne artikel vil vi ikke gå i detaljer om selve de reaktive applikationer, hvilket er en ny funktion i Spring 5-rammen. Sørg for at tjekke artiklen Intro to Reactor Core for flere detaljer.

2. Maven-opsætning

Vi bruger Spring Boot-startere til at starte vores projekt sammen med alle nødvendige afhængigheder.

Den grundlæggende opsætning kræver en overordnet erklæring, afhængighed af webstarter og sikkerhedsstarter. Vi har også brug for Spring Security test framework:

 org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-security org.springframework.security spring-security- test test 

Vi kan tjekke den aktuelle version af Spring Boot-sikkerhedsstarter på Maven Central.

3. Opsætning af projekt

3.1. Bootstrapping af den reaktive applikation

Vi bruger ikke standarden @SpringBootApplication konfiguration, men konfigurer i stedet en Netty-baseret webserver. Netty er en asynkron NIO-baseret ramme, som er et godt fundament for reaktive applikationer.

Det @EnableWebFlux annotation muliggør standard Spring Web Reactive-konfiguration for applikationen:

@ComponentScan (basePackages = {"com.baeldung.security"}) @EnableWebFlux offentlig klasse SpringSecurity5Application {public static void main (String [] args) {try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (SpringSecurity) NettyContext.class) .onClose (). Blok (); }}

Her opretter vi en ny applikationskontekst og venter på, at Netty lukker ned ved at ringe .onLuk (). blok () kæde på Netty-sammenhængen.

Når Netty er lukket ned, lukkes konteksten automatisk ved hjælp af prøv med ressourcer blok.

Vi bliver også nødt til at oprette en Netty-baseret HTTP-server, en handler til HTTP-anmodningerne og adapteren mellem serveren og handler:

@Bean public NettyContext nettyContext (ApplicationContext context) {HttpHandler handler = WebHttpHandlerBuilder .applicationContext (context) .build (); ReactorHttpHandlerAdapter adapter = ny ReactorHttpHandlerAdapter (handler); HttpServer httpServer = HttpServer.create ("localhost", 8080); returner httpServer.newHandler (adapter) .block (); }

3.2. Spring Security Configuration Class

For vores grundlæggende Spring Security-konfiguration opretter vi en konfigurationsklasse - Sikkerhedskonfig.

For at aktivere WebFlux support i Spring Security 5 behøver vi kun at specificere @EnableWebFluxSecurity kommentar:

@EnableWebFluxSecurity offentlig klasse SecurityConfig {// ...}

Nu kan vi drage fordel af klassen ServerHttpSikkerhed at opbygge vores sikkerhedskonfiguration.

Denne klasse er en ny funktion i Spring 5. Det ligner HttpSikkerhed builder, men det er kun aktiveret til WebFlux-applikationer.

Det ServerHttpSikkerhed er allerede forudkonfigureret med nogle fornuftige standarder, så vi kunne springe denne konfiguration helt over. Men til at begynde med giver vi følgende minimale konfiguration:

@Bean offentlig SecurityWebFilterChain sikkerhedgWebFilterChain (ServerHttpSecurity http) {returner http.authorizeExchange () .anyExchange (). Godkendt () .og (). Build (); }

Vi har også brug for en brugeroplysningstjeneste. Spring Security giver os en bekvem mock-brugerbygger og en implementering i hukommelsen af ​​brugeroplysningstjenesten:

@Bean public MapReactiveUserDetailsService userDetailsService () {UserDetails user = User .withUsername ("user") .password (passwordEncoder (). Encode ("password")) .roles ("USER") .build (); returner ny MapReactiveUserDetailsService (bruger); }

Da vi er i reaktivt land, skal brugerdetaljer-tjenesten også være reaktiv. Hvis vi tjekker ReactiveUserDetailsService interface, vi ser, at det er findByUsername metode returnerer faktisk en Mono forlægger:

offentlig grænseflade ReactiveUserDetailsService {Mono findByUsername (String username); }

Nu kan vi køre vores applikation og overholde en almindelig HTTP-grundlæggende godkendelsesformular.

4. Stylet loginformular

En lille, men slående forbedring i Spring Security 5 er en ny stylet loginformular, der bruger Bootstrap 4 CSS-rammen. Stilarkene i loginformularen linker til CDN, så vi kan kun se forbedringen, når vi har forbindelse til internettet.

For at bruge den nye loginformular, lad os tilføje den tilsvarende formLogin () bygmester metode til ServerHttpSikkerhed Bygger:

offentlig SecurityWebFilterChain sikkerhedgWebFilterChain (ServerHttpSecurity http) {returner http.authorizeExchange () .anyExchange (). godkendt (). og (). formLogin (). og (). build (); }

Hvis vi nu åbner hovedsiden i applikationen, ser vi, at den ser meget bedre ud end den standardformular, vi er vant til siden tidligere versioner af Spring Security:

Bemærk, at dette ikke er en produktionsklar form, men det er en god bootstrap for vores applikation.

Hvis vi nu logger ind og derefter går til // localhost: 8080 / logout URL, ser vi bekræftelsesformularen til aflogning, som også er stylet.

5. Sikkerhed med reaktiv controller

For at se noget bag godkendelsesformularen, lad os implementere en simpel reaktiv controller, der hilser brugeren:

@RestController public class GreetController {@GetMapping ("/") public Mono greet (Mono principal) {return principal .map (Principal :: getName) .map (name -> String.format ("Hello,% s", name) ); }}

Efter at have logget ind ser vi hilsenen. Lad os tilføje en anden reaktiv handler, der kun er tilgængelig af admin:

@GetMapping ("/ admin") offentlig Mono greetAdmin (Mono principal) {returner principal .map (Principal :: getName) .map (name -> String.format ("Admin access:% s", name)); }

Lad os nu oprette en anden bruger med rollen ADMIN: i vores brugeroplysningstjeneste:

UserDetails admin = User.withDefaultPasswordEncoder (). Brugernavn ("admin"). Adgangskode ("password") .roles ("ADMIN") .build ();

Vi kan nu tilføje en matcherregel til admin-URL'en, der kræver, at brugeren har ROLE_ADMIN myndighed.

Bemærk, at vi skal sætte matchere før .anyExchange () kædeopkald. Dette opkald gælder for alle andre webadresser, som endnu ikke var dækket af andre matchere:

returner http.authorizeExchange () .pathMatchers ("/ admin"). hasAuthority ("ROLE_ADMIN") .anyExchange (). godkendt () .og (). formLogin (). og (). build ();

Hvis vi nu logger ind med bruger eller admin, vi ser, at de begge overholder den første hilsen, da vi har gjort det tilgængeligt for alle godkendte brugere.

Men kun den admin brugeren kan gå til // localhost: 8080 / admin URL og se hendes hilsen.

6. Sikkerhed med reaktiv metode

Vi har set, hvordan vi kan sikre URL'erne, men hvad med metoder?

For at aktivere metodebaseret sikkerhed for reaktive metoder behøver vi kun tilføje @EnableReactiveMethodSecurity kommentar til vores Sikkerhedskonfig klasse:

@EnableWebFluxSecurity @EnableReactiveMethodSecurity offentlig klasse SecurityConfig {// ...}

Lad os nu oprette en reaktiv hilsen-tjeneste med følgende indhold:

@Service offentlig klasse GreetService {offentlig Mono hilsen () {returner Mono.just ("Hej fra tjenesten!"); }}

Vi kan injicere det i controlleren, gå til // localhost: 8080 / greetService og se, at det rent faktisk fungerer:

@RestController offentlig klasse GreetController {privat GreetService greetService @GetMapping ("/ greetService") offentlig Mono greetService () {return greetService.greet (); } // standardkonstruktører ...}

Men hvis vi nu tilføjer @PreAuthorize anmærkning om servicemetoden med ADMIN rolle, så er hilsen-tjenestens URL ikke tilgængelig for en almindelig bruger:

@Service offentlig klasse GreetService {@PreAuthorize ("hasRole ('ADMIN')") offentlig Mono hilsen () {// ...}

7. Mocking af brugere i test

Lad os se, hvor let det er at teste vores reaktive forårsprogram.

Først opretter vi en test med en injiceret applikationskontekst:

@ContextConfiguration (klasser = SpringSecurity5Application.class) offentlig klasse SecurityTest {@Autowired ApplicationContext-kontekst; // ...}

Nu opretter vi en simpel reaktiv webtestklient, som er en funktion i Spring 5-testrammen:

@Før opsætning af offentlig ugyldighed () {this.rest = WebTestClient .bindToApplicationContext (this.context) .configureClient () .build (); }

Dette giver os mulighed for hurtigt at kontrollere, at den uautoriserede bruger omdirigeres fra hovedsiden i vores applikation til login-siden:

@Test offentlig ugyldig nårNoCredentials_thenRedirectToLogin () {this.rest.get () .uri ("/") .exchange () .expectStatus (). Is3xxRedirection (); }

Hvis vi nu tilføjer @MockWithUser kommentar til en testmetode, kan vi give en godkendt bruger til denne metode.

Denne brugers login og adgangskode ville være bruger og adgangskode henholdsvis, og rollen er BRUGER. Dette kan naturligvis alt sammen konfigureres med @MockWithUser annoteringsparametre.

Nu kan vi kontrollere, at den autoriserede bruger ser hilsenen:

@Test @WithMockUser offentlig ugyldig nårHasCredentials_thenSeesGreeting () {this.rest.get () .uri ("/") .exchange () .expectStatus (). IsOk () .expectBody (String.class) .isEqualTo ("Hej, bruger "); }

Det @WithMockUser annotering er tilgængelig siden Spring Security 4. I Spring Security 5 blev den imidlertid også opdateret til at dække reaktive slutpunkter og metoder.

8. Konklusion

I denne vejledning har vi opdaget nye funktioner i den kommende Spring Security 5-udgivelse, især i den reaktive programmeringsarena.

Som altid er kildekoden til artiklen tilgængelig på GitHub.