Meerdere toegangspunten in Spring Security

1. Overzicht

In deze korte tutorial gaan we kijken hoe definieer meerdere toegangspunten in een Spring Security-applicatie.

Dit houdt voornamelijk het definiëren van meerdere in http blokken in een XML-configuratiebestand of meerdere HttpSecurity instanties door de extensie WebSecurityConfigurerAdapter les meerdere keren.

2. Maven afhankelijkheden

Voor ontwikkeling hebben we de volgende afhankelijkheden nodig:

 org.springframework.boot spring-boot-starter-security 2.2.2.RELEASE org.springframework.boot spring-boot-starter-web 2.2.2.RELEASE org.springframework.boot spring-boot-starter-thymeleaf 2.2.2. RELEASE org.springframework.boot spring-boot-starter-test 2.2.2.RELEASE org.springframework.security spring-security-test 5.2.2.RELEASE 

De nieuwste versies van spring-boot-starter-security, spring-boot-starter-web, spring-boot-starter-thymeleaf, spring-boot-starter-test, spring-security-test kunnen worden gedownload van Maven Central.

3. Meerdere toegangspunten

3.1. Meerdere toegangspunten met meerdere HTTP-elementen

Laten we de hoofdconfiguratieklasse definiëren die een gebruikersbron zal bevatten:

@Configuration @EnableWebSecurity openbare klasse MultipleEntryPointsSecurityConfig {@Bean openbare UserDetailsService userDetailsService () genereert uitzondering {InMemoryUserDetailsManager manager = nieuwe InMemoryUserDetailsManager (); manager.createUser (User .withUsername ("user") .password (encoder (). encode ("userPass")) .roles ("USER"). build ()); manager.createUser (Gebruiker .withUsername ("admin") .password (encoder (). encode ("adminPass")) .roles ("ADMIN"). build ()); terugkeer manager; } @Bean public PasswordEncoder encoder () {retourneer nieuwe BCryptPasswordEncoder (); }}

Laten we nu eens kijken hoe we meerdere ingangspunten kunnen definiëren in onze beveiligingsconfiguratie.

We gaan hier een voorbeeld gebruiken dat wordt aangestuurd door basisverificatie, en daar gaan we goed gebruik van maken Spring Security ondersteunt de definitie van meerdere HTTP-elementen in onze configuraties.

Wanneer u Java-configuratie gebruikt, is de manier om meerdere beveiligingsdomeinen te definiëren meerdere @Configuratie klassen die het WebSecurityConfigurerAdapter basisklasse - elk met zijn eigen beveiligingsconfiguratie. Deze klassen kunnen statisch zijn en in de hoofdconfiguratie worden geplaatst.

De belangrijkste motivatie voor het hebben van meerdere toegangspunten in één applicatie is of er verschillende soorten gebruikers zijn die toegang hebben tot verschillende delen van de applicatie.

Laten we een configuratie definiëren met drie toegangspunten, elk met verschillende machtigingen en authenticatiemodi:

  • een voor beheerders die HTTP-basisverificatie gebruiken
  • een voor gewone gebruikers die formulierverificatie gebruiken
  • en een voor gastgebruikers die geen authenticatie nodig hebben

Het toegangspunt dat is gedefinieerd voor beheerders, beveiligt URL's van het formulier /beheerder/** om alleen gebruikers toe te staan ​​met de rol van ADMIN en vereist HTTP-basisverificatie met een toegangspunt van het type BasicAuthenticationEntryPoint dat is ingesteld met de authenticationEntryPoint () methode:

@Configuration @Order (1) openbare statische klasse App1ConfigurationAdapter breidt WebSecurityConfigurerAdapter uit {@Override protected void configure (HttpSecurity http) gooit uitzondering {http.antMatcher ("/ admin / **") .authorizeRequests (). AnyRequest (). HasRole (" ADMIN ") .en (). HttpBasic (). AuthenticationEntryPoint (authenticationEntryPoint ()); } @Bean openbare AuthenticationEntryPoint authenticationEntryPoint () {BasicAuthenticationEntryPoint entryPoint = nieuwe BasicAuthenticationEntryPoint (); entryPoint.setRealmName ("admin rijk"); retour entryPoint; }}

De @Bestellen annotatie op elke statische klasse geeft de volgorde aan waarin de configuraties worden overwogen om er een te vinden die overeenkomt met de gevraagde URL. De bestellen waarde voor elke klasse moet uniek zijn.

De boon van het type BasicAuthenticationEntryPoint vereist de eigenschap echte naam worden ingesteld.

3.2. Meerdere toegangspunten, hetzelfde HTTP-element

Laten we vervolgens de configuratie voor URL's van het formulier definiëren /gebruiker/** waartoe gewone gebruikers met een gebruikersrol toegang hebben met behulp van formulierauthenticatie:

@Configuration @Order (2) openbare statische klasse App2ConfigurationAdapter breidt WebSecurityConfigurerAdapter uit {protected void configure (HttpSecurity http) genereert uitzondering {http.antMatcher ("/ user / **") .authorizeRequests (). AnyRequest (). HasRole ("USER" ) .and () // formLogin-configuratie .and () .exceptionHandling () .defaultAuthenticationEntryPointFor (loginUrlauthenticationEntryPointWithWarning (), nieuw AntPathRequestMatcher ("/ user / private / **")) .defaultAuthenticationEntryPointFor (loginUrlauthenticationEntryPointWithWarning (), nieuw AntPathRequestMatcher ("/ user / private / **")) .defaultAuthenticationEntryPoint (loginUrlaathhenticationEntryPoint (loginUrlaathhenticationEntryPoint (loginUrlaathhentication) gebruiker / algemeen / ** ")); }}

Zoals we kunnen zien, is een andere manier om toegangspunten te definiëren, naast de methode authenticationEntryPoint (), door de defaultAuthenticationEntryPointFor () methode. Dit kan meerdere ingangspunten definiëren die voldoen aan verschillende voorwaarden op basis van een RequestMatcher voorwerp.

De RequestMatcher interface heeft implementaties op basis van verschillende soorten voorwaarden, zoals bijpassend pad, mediatype of regexp. In ons voorbeeld hebben we de AntPathRequestMatch gebruikt om twee verschillende ingangspunten in te stellen voor URL's van de formulieren / gebruiker / privé / ** en / gebruiker / algemeen / **.

Vervolgens moeten we de ingangspunten bonen in dezelfde statische configuratieklasse definiëren:

@Bean openbare AuthenticationEntryPoint loginUrlauthenticationEntryPoint () {retourneer nieuwe LoginUrlAuthenticationEntryPoint ("/ userLogin"); } @Bean openbare AuthenticationEntryPoint loginUrlauthenticationEntryPointWithWarning () {retourneer nieuwe LoginUrlAuthenticationEntryPoint ("/ userLoginWithWarning"); }

Het belangrijkste punt hier is hoe u deze meerdere toegangspunten instelt - niet noodzakelijk de implementatiedetails van elk.

In dit geval zijn de ingangspunten beide van het type LoginUrlAuthenticationEntryPoint, en gebruik een andere inlogpagina-URL: /gebruiker login voor een eenvoudige inlogpagina en / userLoginWithWarning voor een aanmeldingspagina die ook een waarschuwing weergeeft wanneer u probeert toegang te krijgen tot het /gebruiker/ privé-URL's.

Deze configuratie vereist ook het definiëren van de /gebruiker login en / userLoginWithWarning MVC-toewijzingen en twee pagina's met een standaard inlogformulier.

Voor de formulierauthenticatie is het erg belangrijk om te onthouden dat elke URL die nodig is voor de configuratie, zoals de inlogverwerkings-URL, ook de /gebruiker/** formaat of anderszins geconfigureerd zijn om toegankelijk te zijn.

Beide bovenstaande configuraties worden omgeleid naar een /403 URL als een gebruiker zonder de juiste rol probeert toegang te krijgen tot een beschermde URL.

Zorg ervoor dat u unieke namen gebruikt voor de bonen, zelfs als ze zich in verschillende statische klassen bevinden, anders overschrijft de ene de andere.

3.3. Nieuw HTTP-element, geen toegangspunt

Laten we tot slot de derde configuratie definiëren voor URL's van het formulier /gast/** waarmee alle soorten gebruikers, inclusief niet-geverifieerde gebruikers:

@Configuration @Order (3) openbare statische klasse App3ConfigurationAdapter breidt WebSecurityConfigurerAdapter uit {protected void configure (HttpSecurity http) gooit uitzondering {http.antMatcher ("/ guest / **"). AuthorizeRequests (). AnyRequest (). AllowAll (); }}

3.4. XML-configuratie

Laten we eens kijken naar de equivalente XML-configuratie voor de drie HttpSecurity instanties in de vorige sectie.

Zoals verwacht zal deze drie aparte XML bevatten blokken.

Voor de /beheerder/** URL's waarvoor de XML-configuratie de instappunt-ref kenmerk van http-basic element:

Hierbij moet worden opgemerkt dat als u XML-configuratie gebruikt, de rollen de vorm moeten hebben ROL_.

De configuratie voor het /gebruiker/** URL's moeten in tweeën worden opgesplitst http blokken in xml omdat er geen direct equivalent is aan de defaultAuthenticationEntryPointFor () methode.

De configuratie voor URL's / gebruiker / algemeen / ** is:

  // form-login configuratie 

Voor de / gebruiker / privé / ** URL's kunnen we een vergelijkbare configuratie definiëren:

  // form-login configuratie 

Voor de /gast/** URL's hebben we de http element:

Ook belangrijk hierbij is dat er minimaal één XML blok moet overeenkomen met het / ** patroon.

4. Toegang tot beveiligde URL's

4.1. MVC-configuratie

Laten we aanvraagtoewijzingen maken die overeenkomen met de URL-patronen die we hebben beveiligd:

@Controller openbare klasse PagesController {@GetMapping ("/ admin / myAdminPage") openbare String getAdminPage () {return "multipleHttpElems / myAdminPage"; } @GetMapping ("/ user / general / myUserPage") public String getUserPage () {return "multipleHttpElems / myUserPage"; } @GetMapping ("/ user / private / myPrivateUserPage") openbare String getPrivateUserPage () {retourneer "multipleHttpElems / myPrivateUserPage"; } @GetMapping ("/ guest / myGuestPage") openbare String getGuestPage () {return "multipleHttpElems / myGuestPage"; } @GetMapping ("/ multipleHttpLinks") openbare String getMultipleHttpLinksPage () {return "multipleHttpElems / multipleHttpLinks"; }}

De / multipleHttpLinks mapping retourneert een eenvoudige HTML-pagina met links naar de beschermde URL's:

Beheerderspagina Gebruikerspagina Privé gebruikerspagina Gastpagina

Elk van de HTML-pagina's die overeenkomen met de beschermde URL's heeft een eenvoudige tekst en een backlink:

Welkom admin! Terug naar links

4.2. Initialiseren van de applicatie

We zullen ons voorbeeld uitvoeren als een Spring Boot-applicatie, dus laten we een klasse definiëren met de hoofdmethode:

@SpringBootApplication openbare klasse MultipleEntryPointsApplication {openbare statische leegte hoofd (String [] args) {SpringApplication.run (MultipleEntryPointsApplication.class, args); }}

Als we de XML-configuratie willen gebruiken, moeten we ook de @ImportResource ({"classpath *: spring-security-multiple-entry.xml"}) annotatie bij onze hoofdklasse.

4.3. De beveiligingsconfiguratie testen

Laten we een JUnit-testklasse opzetten die we kunnen gebruiken om onze beschermde URL's te testen:

@RunWith (SpringRunner.class) @WebAppConfiguration @SpringBootTest (classes = MultipleEntryPointsApplication.class) openbare klasse MultipleEntryPointsTest {@Autowired private WebApplicationContext wac; @Autowired privé FilterChainProxy springSecurityFilterChain; privé MockMvc mockMvc; @Before public void setup () {this.mockMvc = MockMvcBuilders.webAppContextSetup (this.wac) .addFilter (springSecurityFilterChain) .build (); }}

Laten we vervolgens de URL's testen met de beheerder gebruiker.

Bij het aanvragen van de / admin / adminPage URL zonder een HTTP-basisauthenticatie, we zouden verwachten een ongeautoriseerde statuscode te ontvangen, en na het toevoegen van de authenticatie zou de statuscode 200 OK moeten zijn.

Als u probeert toegang te krijgen tot het / user / userPage URL met de admin-gebruiker, we zouden status 302 Forbidden moeten krijgen:

@Test public void whenTestAdminCredentials_thenOk () gooit Uitzondering {mockMvc.perform (get ("/ admin / myAdminPage")). AndExpect (status (). IsUnauthorized ()); mockMvc.perform (get ("/ admin / myAdminPage") .with (httpBasic ("admin", "adminPass"))). andExpect (status (). isOk ()); mockMvc.perform (get ("/ user / myUserPage") .with (user ("admin"). wachtwoord ("adminPass"). rollen ("ADMIN"))) .andExpect (status (). isForbidden ()); }

Laten we een vergelijkbare test maken met de normale gebruikersreferenties om toegang te krijgen tot de URL's:

@Test public void whenTestUserCredentials_thenOk () gooit Uitzondering {mockMvc.perform (get ("/ user / general / myUserPage")). AndExpect (status (). IsFound ()); mockMvc.perform (get ("/ user / general / myUserPage") .with (user ("user"). wachtwoord ("userPass"). rollen ("USER"))) .andExpect (status (). isOk () ); mockMvc.perform (get ("/ admin / myAdminPage") .with (user ("user"). wachtwoord ("userPass"). rollen ("USER"))) .andExpect (status (). isForbidden ()); }

In de tweede test kunnen we zien dat het missen van de formulierauthenticatie zal resulteren in de status 302 Gevonden in plaats van Niet geautoriseerd, aangezien Spring Security zal doorverwijzen naar het inlogformulier.

Laten we tot slot een test maken waarin we toegang krijgen tot het / guest / guestPage URL zal alle drie soorten authenticatie en verifiëren dat we een status van 200 OK ontvangen:

@Test openbare leegte gegevenAnyUser_whenGetGuestPage_thenOk () gooit uitzondering {mockMvc.perform (get ("/ guest / myGuestPage")). AndExpect (status (). IsOk ()); mockMvc.perform (get ("/ guest / myGuestPage") .with (user ("user"). wachtwoord ("userPass"). rollen ("USER"))) .andExpect (status (). isOk ()); mockMvc.perform (get ("/ guest / myGuestPage") .with (httpBasic ("admin", "adminPass"))) .andExpect (status (). isOk ()); }

5. Conclusie

In deze zelfstudie hebben we laten zien hoe u meerdere toegangspunten configureert bij het gebruik van Spring Security.

De volledige broncode voor de voorbeelden is te vinden op GitHub. Om de toepassing uit te voeren, verwijdert u het commentaar op het MultipleEntryPointsApplicationbegin de les tag in het pom.xml en voer het commando uit mvn spring-boot: runen opent vervolgens het / multipleHttpLinks URL.

Merk op dat het niet mogelijk is om uit te loggen bij het gebruik van HTTP Basic Authentication, dus u zult de browser moeten sluiten en opnieuw openen om deze authenticatie te verwijderen.

Gebruik het gedefinieerde Maven-profiel om de JUnit-test uit te voeren toegangspunten met het volgende commando:

mvn schone installatie -PentryPoints