Einfaches Single Sign-On mit Spring Security OAuth2

1. Übersicht

In diesem Tutorial wird erläutert, wie Sie SSO - Single Sign On - mithilfe von Spring Security OAuth und Spring Boot unter Verwendung von Keycloak als Autorisierungsserver implementieren .

Wir werden 4 separate Anwendungen verwenden:

  • Ein Autorisierungsserver - dies ist der zentrale Authentifizierungsmechanismus
  • Ein Ressourcenserver - der Anbieter von Foo s
  • Zwei Client-Anwendungen - die Anwendungen, die SSO verwenden

Ganz einfach ausgedrückt: Wenn ein Benutzer versucht, über eine Client-App auf eine Ressource zuzugreifen, wird er über den Autorisierungsserver zur Authentifizierung umgeleitet. Keycloak meldet den Benutzer an. Wenn auf die zweite Client-App mit demselben Browser zugegriffen wird, muss der Benutzer seine Anmeldeinformationen nicht erneut eingeben, während er noch in der ersten App angemeldet ist.

Wir werden den Berechtigungstyp Grant-Typ aus OAuth2 verwenden, um die Delegierung der Authentifizierung voranzutreiben.

Wir werden den OAuth-Stack in Spring Security 5 verwenden. Wenn Sie den OAuth-Legacy-Stack von Spring Security verwenden möchten, lesen Sie den vorherigen Artikel: Einfache einmalige Anmeldung mit Spring Security OAuth2 (Legacy-Stack)

Gemäß Migrationsleitfaden:

Spring Security bezeichnet diese Funktion als OAuth 2.0-Anmeldung, während Spring Security OAuth sie als SSO bezeichnet

Okay, lass uns gleich hineinspringen.

2. Der Autorisierungsserver

Zuvor bot der Spring Security OAuth-Stack die Möglichkeit, einen Autorisierungsserver als Spring-Anwendung einzurichten.

Der OAuth-Stack wurde jedoch von Spring nicht mehr unterstützt und jetzt verwenden wir Keycloak als Autorisierungsserver.

Dieses Mal richten wir unseren Autorisierungsserver als eingebetteten Keycloak-Server in einer Spring Boot-App ein .

In unserer Vorkonfiguration definieren wir zwei Clients, ssoClient-1 und ssoClient-2 , einen für jede Clientanwendung .

3. Der Ressourcenserver

Als nächstes benötigen wir einen Ressourcenserver oder die REST-API, die uns die Foos liefert, die unsere Client-App verbraucht.

Es ist im Wesentlichen dasselbe, das wir zuvor für unsere Angular Client Apps verwendet haben.

4. Die Client-Anwendungen

Schauen wir uns nun unsere Thymeleaf-Client-Anwendung an. Wir werden natürlich Spring Boot verwenden, um die Konfiguration zu minimieren.

Denken Sie daran, dass wir zwei davon benötigen, um die Single Sign-On-Funktionalität zu demonstrieren .

4.1. Maven-Abhängigkeiten

Zunächst benötigen wir die folgenden Abhängigkeiten in unserer pom.xml :

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-oauth2-client   org.springframework.boot spring-boot-starter-thymeleaf   org.thymeleaf.extras thymeleaf-extras-springsecurity5   org.springframework spring-webflux   io.projectreactor.netty reactor-netty 

Um die gesamte benötigte Client-Unterstützung einschließlich der Sicherheit einzuschließen , müssen wir lediglich den Spring-Boot-Starter-Oauth2-Client hinzufügen . Da das alte RestTemplate veraltet sein wird, werden wir WebClient verwenden , und deshalb haben wir Spring-Webflux und Reactor-Netty hinzugefügt .

4.2. Sicherheitskonfiguration

Als nächstes der wichtigste Teil, die Sicherheitskonfiguration unserer ersten Client-Anwendung:

@EnableWebSecurity public class UiSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/") .permitAll() .anyRequest() .authenticated() .and() .oauth2Login(); } @Bean WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository); oauth2.setDefaultOAuth2AuthorizedClient(true); return WebClient.builder().apply(oauth2.oauth2Configuration()).build(); } }

Der Kern dieser Konfiguration ist die Methode oauth2Login () , mit der die OAuth 2.0- Anmeldeunterstützung von Spring Security aktiviert wird. Da wir Keycloak verwenden, eine Standardanmeldelösung für Webanwendungen und RESTful-Webdienste, müssen wir keine weitere Konfiguration für SSO hinzufügen.

Schließlich haben wir auch eine WebClient- Bean definiert , die als einfacher HTTP-Client für die Verarbeitung von Anforderungen fungiert, die an unseren Ressourcenserver gesendet werden sollen.

Und hier ist die application.yml :

spring: security: oauth2: client: registration: custom: client-id: ssoClient-1 client-secret: ssoClientSecret-1 scope: read,write authorization-grant-type: authorization_code redirect-uri: //localhost:8082/ui-one/login/oauth2/code/custom provider: custom: authorization-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/auth token-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/token user-info-uri: //localhost:8083/auth/realms/baeldung/protocol/openid-connect/userinfo user-name-attribute: preferred_username thymeleaf: cache: false server: port: 8082 servlet: context-path: /ui-one resourceserver: api: project: url: //localhost:8081/sso-resource-server/api/foos/ 

Hier ist spring.security.oauth2.client.registration der Root-Namespace für die Registrierung eines Clients. Wir haben einen Kunden mit einer benutzerdefinierten Registrierungs-ID definiert . Dann haben wir die Client-ID , das Client-Geheimnis , den Bereich , den Typ der Autorisierungsgewährung und die Weiterleitungs-URL definiert , die natürlich mit der für unseren Autorisierungsserver definierten identisch sein sollten.

Danach haben wir unseren Dienstanbieter oder den Autorisierungsserver ebenfalls mit derselben benutzerdefinierten ID definiert und die verschiedenen URIs aufgelistet, die Spring Security verwenden soll. Das ist alles, was wir definieren müssen, und das Framework erledigt den gesamten Anmeldevorgang, einschließlich der Umleitung zu Keycloak, nahtlos für uns .

Beachten Sie auch, dass wir in unserem Beispiel hier unseren Autorisierungsserver eingeführt haben, aber natürlich auch andere Drittanbieter wie Facebook oder GitHub verwenden können.

4.3. Der Controller

Implementieren wir nun unseren Controller in der Client-App, um von unserem Ressourcenserver nach Foo s zu fragen :

@Controller public class FooClientController { @Value("${resourceserver.api.url}") private String fooApiUrl; @Autowired private WebClient webClient; @GetMapping("/foos") public String getFoos(Model model) { List foos = this.webClient.get() .uri(fooApiUrl) .retrieve() .bodyToMono(new ParameterizedTypeReference
    
     () { }) .block(); model.addAttribute("foos", foos); return "foos"; } }
    

Wie wir sehen können, haben wir hier nur eine Methode, mit der die Ressourcen für die foos- Vorlage verteilt werden. Wir mussten keinen Code für die Anmeldung hinzufügen.

4.4. Vorderes Ende

Schauen wir uns nun die Front-End-Konfiguration unserer Client-Anwendung an. Wir werden uns hier nicht darauf konzentrieren, hauptsächlich, weil wir bereits auf der Website behandelt haben.

Unsere Kundenanwendung hier hat ein sehr einfaches Frontend; Hier ist die index.html :

Spring OAuth Client Thymeleaf - 1 Welcome !

Login

Und die foos.html :

Spring OAuth Client Thymeleaf -1 Hi, preferred_username   
    
ID Name
No foos
ID Name

Auf der Seite foos.html müssen die Benutzer authentifiziert sein. Wenn ein nicht authentifizierter Benutzer versucht, auf foos.html zuzugreifen , wird er zuerst zur Anmeldeseite von Keycloak umgeleitet .

4.5. Die zweite Client-Anwendung

We'll configure a second application, Spring OAuth Client Thymeleaf -2 using another client_idssoClient-2.

It'll mostly be the same as the first application we just described.

The application.yml will differ to include a different client_id, client_secret and redirect_uri in its spring.security.oauth2.client.registration:

spring: security: oauth2: client: registration: custom: client-id: ssoClient-2 client-secret: ssoClientSecret-2 scope: read,write authorization-grant-type: authorization_code redirect-uri: //localhost:8084/ui-two/login/oauth2/code/custom

And, of course, we need to have a different server port for it as well, so that we can run them in parallel:

server: port: 8084 servlet: context-path: /ui-two

Finally, we'll tweak the front end HTMLs to have a title as Spring OAuth Client Thymeleaf – 2 instead of – 1 so that we can distinguish between the two.

5. Testing SSO Behavior

To test SSO behavior, let's run our Applications.

We'll need all our 4 Boot Apps – the Authorization Server, the Resource Server and both Client Applications – to be up and running for this.

Now let's open up a browser, say Chrome, and log in to Client-1 using the credentials [email protected]/123. Next, in another window or tab, hit the URL for Client-2. On clicking the login button, we'll be redirected to the Foos page straightaway, bypassing the authentication step.

Similarly, if the user logs in to Client-2 first, they need not enter their username/password for Client-1.

6. Conclusion

In diesem Tutorial haben wir uns auf die Implementierung von Single Sign-On mit Spring Security OAuth2 und Spring Boot mit Keycloak als Identitätsanbieter konzentriert.

Wie immer finden Sie den vollständigen Quellcode auf GitHub.