Spring Security 5 - OAuth2 Prijava

1. Pregled

Spring Security 5 predstavlja novu OAuth2LoginConfigurer klase koju možemo koristiti za konfiguriranje vanjskog poslužitelja za autorizaciju.

U ovom članku, istražit ćemo neke od različitih konfiguracijskih opcija dostupnih za oauth2Login () element.

2. Ovisnosti Mavena

U projektu Spring Boot, sve što trebamo je dodati starter spring-boot-starter-oauth2-client:

 org.springframework.boot spring-boot-starter-oauth2-client 2.3.3.OSLOBODI 

U projektu koji se ne pokreće, uz standardne ovisnosti Spring i Spring Security, trebat ćemo izričito dodati i proljeće-sigurnost-oauth2-klijent i proljeće-sigurnost-oauth2-jose ovisnosti:

 org.springframework.security spring-security-oauth2-client 5.3.4.RELEASE org.springframework.security spring-security-oauth2-jose 5.3.4.RELEASE 

3. Postavljanje klijenata

U projektu Spring Boot, sve što trebamo učiniti je dodati nekoliko standardnih svojstava za svakog klijenta kojeg želimo konfigurirati.

Postavimo svoj projekt za prijavu s klijentima registriranim na Googleu i Facebooku kao pružateljima autentifikacije.

3.1. Dobivanje vjerodajnica klijenta

Da biste dobili vjerodajnice klijenta za provjeru autentičnosti Google OAuth2, idite na Google API konzolu - odjeljak "Vjerodajnice".

Ovdje ćemo stvoriti vjerodajnice tipa “OAuth2 Client ID” za našu web aplikaciju. To rezultira time da Google za nas postavlja tajni ID klijenta.

Također moramo konfigurirati ovlašteni URI za preusmjeravanje u Google konzoli, što je put na koji će korisnici biti preusmjereni nakon što se uspješno prijave s Googleom.

Prema zadanim postavkama Spring Boot konfigurira ovaj URI za preusmjeravanje kao / login / oauth2 / code / {registrationId}. Stoga ćemo za Google dodati URI:

// localhost: 8081 / login / oauth2 / code / google

Da bismo dobili vjerodajnice klijenta za provjeru autentičnosti putem Facebooka, moramo registrirati aplikaciju na web mjestu Facebook for Developers i postaviti odgovarajući URI kao „Valid OAuth URI preusmjeravanja“:

// localhost: 8081 / login / oauth2 / code / facebook

3.3. Konfiguracija sigurnosti

Dalje, trebamo dodati vjerodajnice klijenta na primjena.svojstva datoteka. Svojstva Spring Security imaju prefiks sa "Spring.security.oauth2.client.registration" slijedi ime klijenta, a zatim ime svojstva klijenta:

spring.security.oauth2.client.registration.google.client-id = spring.security.oauth2.client.registration.google.client-secret = spring.security.oauth2.client.registration.facebook.client-id = spring. security.oauth2.client.registration.facebook.client-secret =

Dodavanjem ovih svojstava za barem jednog klijenta omogućit će se Oauth2ClientAutoConfiguration razred koji postavlja sav potreban grah.

Automatska konfiguracija web sigurnosti jednaka je definiranju jednostavnog oauth2Login () element:

@Configuration javna klasa SecurityConfig proširuje WebSecurityConfigurerAdapter {@Override zaštićena void konfiguracija (HttpSecurity http) baca izuzetak {http.authorizeRequests () .anyRequest (). Authenticated () .and () .oauth2Login (); }}

Ovdje možemo vidjeti oauth2Login () element koristi se na sličan način kao što je već poznato httpBasic () i formLogin () elementi.

Sada, kada pokušamo pristupiti zaštićenom URL-u, aplikacija će prikazati automatski generiranu stranicu za prijavu s dva klijenta:

3.4. Ostali klijenti

Imajte na umu da osim Googlea i Facebooka, projekt Spring Security sadrži i zadane konfiguracije za GitHub i Okta. Te zadane konfiguracije pružaju sve potrebne podatke za provjeru autentičnosti, što nam omogućuje samo unos vjerodajnica klijenta.

Ako želimo koristiti drugog davatelja usluga provjere autentičnosti koji nije konfiguriran u Spring Security, morat ćemo definirati potpunu konfiguraciju s podacima kao što su URI autorizacije i URI tokena. Evo pogleda zadanih konfiguracija u Spring Security da biste imali ideju o potrebnim svojstvima.

4. Postavljanje u projektu koji se ne pokreće

4.1. Stvaranje a ClientRegistrationRepository Grah

Ako ne radimo s aplikacijom Spring Boot, morat ćemo definirati ClientRegistrationRepository grah koji sadrži interni prikaz podataka o klijentu u vlasništvu poslužitelja za autorizaciju:

@Configuration @EnableWebSecurity @PropertySource ("classpath: application.properties") javna klasa SecurityConfig proširuje WebSecurityConfigurerAdapter {private static list clients = Arrays.asList ("google", "facebook"); @Bean javni ClientRegistrationRepository clientRegistrationRepository () {Popis registracija = klijenti.stream () .map (c -> getRegistration (c)) .filter (registracija -> registracija! = Null) .collect (Collectors.toList ()); vratiti novi InMemoryClientRegistrationRepository (registracije); }}

Ovdje stvaramo InMemoryClientRegistrationRepository s popisom ClientRegistration predmeta.

4.2. Zgrada ClientRegistration Predmeti

Da vidimo getRegistration () metoda koja gradi ove objekte:

privatni statički niz CLIENT_PROPERTY_KEY = "spring.security.oauth2.client.registration."; @Autowired private Environment env; privatna ClientRegistration getRegistration (String klijent) {String clientId = env.getProperty (CLIENT_PROPERTY_KEY + client + ".client-id"); if (clientId == null) {return null; } Niz clientSecret = env.getProperty (CLIENT_PROPERTY_KEY + client + ".client-secret"); if (client.equals ("google")) {return CommonOAuth2Provider.GOOGLE.getBuilder (client) .clientId (clientId) .clientSecret (clientSecret) .build (); } if (client.equals ("facebook")) {return CommonOAuth2Provider.FACEBOOK.getBuilder (client) .clientId (clientId) .clientSecret (clientSecret) .build (); } return null; }

Ovdje čitamo vjerodajnice klijenta sa sličnog primjena.svojstva datoteku, a zatim pomoću CommonOauth2Provider enum već definiran u Spring Security za ostatak svojstava klijenta za Google i Facebook klijente.

Svaki ClientRegistration instanca odgovara klijentu.

4.3. Registriranje ClientRegistrationRepository

Napokon, moramo stvoriti OAuth2AuthorizedClientService grah na bazi ClientRegistrationRepository grah i oba registrirajte na oauth2Login () element:

@Override zaštićena void konfiguracija (HttpSecurity http) baca izuzetak {http.authorizeRequests (). AnyRequest (). Authenticated () .and () .oauth2Login () .clientRegistrationRepository (clientRegistrationRepository ()) .authorizedClientService (odobreniClientService (). } @Bean public OAuth2AuthorizedClientService odobrioClientService () {return new InMemoryOAuth2AuthorizedClientService (clientRegistrationRepository ()); }

Kao što je ovdje dokazano, možemo koristiti clientRegistrationRepository () metoda oauth2Login () za registraciju prilagođenog spremišta za registraciju.

Morat ćemo definirati i prilagođenu stranicu za prijavu jer se više neće automatski generirati. O tome ćemo vidjeti više informacija u sljedećem odjeljku.

Nastavimo s daljnjim prilagođavanjem našeg postupka prijave.

5. Prilagođavanje oauth2Login ()

Postoji nekoliko elemenata koje postupak OAuth 2 koristi i koje možemo prilagoditi korištenjem oauth2Login () metode.

Imajte na umu da svi ti elementi imaju zadane konfiguracije u Spring Boot-u i da nije potrebna eksplicitna konfiguracija.

Pogledajmo kako ih možemo prilagoditi u našoj konfiguraciji.

5.1. Stranica za prilagođenu prijavu

Iako Spring Boot generira zadanu stranicu za prijavu za nas, obično ćemo htjeti definirati vlastitu prilagođenu stranicu.

Počnimo s konfiguriranjem novog URL-a za prijavu za oauth2Login () element pomoćuloginPage () metoda:

@Override zaštićena void konfiguracija (HttpSecurity http) baca izuzetak {http.authorizeRequests () .antMatchers ("/ oauth_login") .permitAll () .anyRequest () .authenticated () .and () .oauth2Login () .loginPage ("/ oauth_login "); }

Ovdje smo postavili URL za prijavu / oauth_login.

Dalje, definirajmo a LoginController s metodom koja se preslikava na ovaj URL:

@Controller javna klasa LoginController {privatni statički niz AutorizacijaRequestBaseUri = "oauth2 / autorizacija"; Karta oauth2AuthenticationUrls = nova HashMap (); @Autowired privatni ClientRegistrationRepository clientRegistrationRepository; @GetMapping ("/ oauth_login") javni niz getLoginPage (model modela) {// ... return "oauth_login"; }}

Ova metoda mora u prikaz poslati kartu dostupnih klijenata i krajnje točke njihovih autorizacija, koju ćemo dobiti od ClientRegistrationRepository grah:

javni String getLoginPage (model modela) {Iterable clientRegistrations = null; ResolvableType type = ResolvableType.forInstance (clientRegistrationRepository) .as (Iterable.class); if (type! = ResolvableType.NONE && ClientRegistration.class.isAssignableFrom (type.resolveGenerics () [0])) {clientRegistrations = (Iterable) clientRegistrationRepository; } clientRegistrations.forEach (registration -> oauth2AuthenticationUrls.put (registration.getClientName (), утверждениеRequestBaseUri + "/" + registration.getRegistrationId ())); model.addAttribute ("urls", oauth2AuthenticationUrls); vrati "oauth_login"; }

Napokon, moramo definirati svoje oauth_login.html stranica:

Prijavi se putem:

Klijent

Ovo je jednostavna HTML stranica koja prikazuje veze za provjeru autentičnosti kod svakog klijenta.

Nakon što mu dodamo malo stylinga, možemo promijeniti izgled stranice za prijavu:

5.2. Uspješno i neuspješno prilagođavanje autentifikacije

Ponašanjem nakon provjere autentičnosti možemo kontrolirati pomoću različitih metoda:

  • defaultSuccessUrl () i failureUrl () - za preusmjeravanje korisnika na zadani URL
  • successHandler () i failureHandler () - izvršiti prilagođenu logiku slijedeći postupak provjere autentičnosti

Pogledajmo kako možemo postaviti prilagođene URL-ove za preusmjeravanje korisnika na:

.oauth2Login () .defaultSuccessUrl ("/ loginSuccess") .failureUrl ("/ loginFailure");

Ako je korisnik posjetio sigurnu stranicu prije autentifikacije, bit će preusmjeren na tu stranicu nakon prijave; u protivnom bit će preusmjereni na / prijavaUspjeh.

Ako želimo da korisnik uvijek bude poslan na / prijavaUspjeh URL, bez obzira jesu li prije bili na zaštićenoj stranici ili ne, možemo koristiti metodu defaultSuccessUrl (“/ loginSuccess”, točno).

Da bismo koristili prilagođeni rukovatelj, morali bismo stvoriti klasu koja implementira AuthenticationSuccessHandler ili AuthenticationFailureHandler sučelja, nadjačati naslijeđene metode, a zatim postaviti grah pomoću successHandler () i neuspjehHandler () metode.

5.3. Krajnja točka prilagođenog odobrenja

Krajnja točka autorizacije krajnja je točka koju Spring Security koristi za pokretanje zahtjeva za autorizacijom na vanjski poslužitelj.

Prvi, postavimo nova svojstva za krajnju točku autorizacije:

.oauth2Login () .authorizationEndpoint () .baseUri ("/ oauth2 / autoriziraj-klijent") .authorizationRequestRepository (odobrenjeRequestRepository ());

Evo, izmijenili smo baseUri do / oauth2 / auth-client umjesto zadanog / oauth2 / autorizacija. Također izričito postavljamo autorizacijaRequestRepository () grah koji moramo definirati:

@Bean public AuthorizationRequestRepository authRequestRepository () {return new HttpSessionOAuth2AuthorizationRequestRepository (); }

U našem smo primjeru koristili implementaciju Springa za naš grah, ali mogli bismo pružiti i prilagođenu.

5.4. Krajnja točka prilagođenog tokena

Žeton krajnja točka obrađuje pristupne tokene.

Izričito konfigurirajmo tokenEndpoint ()sa zadanom realizacijom klijenta odgovora:

.oauth2Login () .tokenEndpoint () .accessTokenResponseClient (accessTokenResponseClient ());

I ovdje je odgovor klijenta graha:

@Bean public OAuth2AccessTokenResponseClient accessTokenResponseClient () {return new NimbusAuthorizationCodeTokenResponseClient (); }

Ova je konfiguracija ista kao zadana i koristi implementaciju Spring koja se temelji na razmjeni autorizacijskog koda s davateljem.

Naravno, mogli bismo zamijeniti i prilagođenog klijenta za odgovor.

5.5. Krajnja točka prilagođenog preusmjeravanja

To je krajnja točka na koju će se preusmjeriti nakon provjere autentičnosti s vanjskim davateljem.

Pogledajmo kako možemo promijeniti baseUri za krajnju točku preusmjeravanja:

.oauth2Login () .redirectionEndpoint () .baseUri ("/ oauth2 / redirect")

Zadani URI je prijava / oauth2 / kod.

Imajte na umu da ako ga promijenimo, također moramo ažurirati redirectUriTemplate svojstvo svakog ClientRegistration i dodajte novi URI kao ovlašteni URI za preusmjeravanje za svakog klijenta.

5.6. Krajnja točka korisničkih podataka

Krajnja točka korisničkih informacija mjesto je koje možemo iskoristiti za dobivanje korisničkih podataka.

Ovu krajnju točku možemo prilagoditi pomoću userInfoEndpoint () metoda. Za to se možemo poslužiti metodama kao što su userService () i customUserType () za izmjenu načina na koji se dohvaćaju korisničke informacije.

6. Pristup korisničkim informacijama

Uobičajeni zadatak koji bismo možda željeli postići je pronalaženje podataka o prijavljenom korisniku. Za ovo, možemo uputiti zahtjev krajnjoj točki korisničkih podataka.

Prvo ćemo morati dobiti klijenta koji odgovara trenutnom korisničkom tokenu:

@Autowired privatni OAuth2AuthorizedClientService odobreniClientService; @GetMapping ("/ loginSuccess") javni niz getLoginInfo (model modela, autentifikacija OAuth2AuthenticationToken) {OAuth2AuthorizedClient client = odobreniClientService .loadAuthorizedClient (authentication.getAuthorizedClientRegistrationId (), authentication.get (N), authentication.get (N); // ... return "loginSuccess"; }

Dalje ćemo poslati zahtjev na krajnju točku korisnikovih podataka o klijentu i dohvatiti Mapa korisničkih atributa:

Niz userInfoEndpointUri = client.getClientRegistration () .getProviderDetails (). GetUserInfoEndpoint (). GetUri (); if (! StringUtils.isEmpty (userInfoEndpointUri)) {RestTemplate restTemplate = novi RestTemplate (); HttpHeaders zaglavlja = novi HttpHeaders (); headers.add (HttpHeaders.AUTHORIZATION, "Donositelj" + client.getAccessToken () .getTokenValue ()); HttpEntity entitet = novi HttpEntity ("", zaglavlja); ResponseEntity odgovor = restTemplate .exchange (userInfoEndpointUri, HttpMethod.GET, entitet, Map.class); Mapa userAttributes = response.getBody (); model.addAttribute ("name", userAttributes.get ("name")); }

Dodavanjem Ime svojstvo kao a Model atribut, možemo ga prikazati u prijavaUspjeh pogledati kao poruku dobrodošlice korisniku:

Osim Ime, the Map korisničkih atributa sadrži i svojstva kao što su e-adresa, obiteljsko ime,slika, lokalizacija.

7. Zaključak

U ovom smo članku vidjeli kako možemo koristiti oauth2Login () element u Spring Security za provjeru autentičnosti kod različitih pružatelja usluga kao što su Google i Facebook. Također smo prošli nekoliko uobičajenih scenarija prilagodbe ovog postupka.

Potpuni izvorni kod primjera može se naći na GitHubu.