Java >> Java Tutorial >  >> Tag >> Spring

So verwenden Sie Spring Security mit SAML-Protokollbindung

In diesem Beitrag werde ich zeigen, wie wir Spring Security mit SAML Protocol Binding verwenden können, um mit Keycloak Identity Provider zu integrieren. Und wenn Sie mehr über die Verwendung von Keycloak erfahren möchten, können Sie hier nachlesen.

Was ist SAML?

SAML steht für Security Assertion Markup Language. Es ist ein offener Standard für
den Austausch von Authentifizierungs- und Autorisierungsdaten zwischen einem Dienstanbieter (SP) und einem Identitätsanbieter (IdP).

Identitätsanbieter – führt die Authentifizierung durch und validiert die Benutzeridentität für die Autorisierung und leitet diese an den Dienstanbieter weiter.

Dienstleister – Vertraut dem Identitätsanbieter und gewährt dem Benutzer basierend auf der Autorisierung Zugriff auf den Dienst.

SAML-Authentifizierungsablauf

Als Teil dieses Ablaufs werden wir eine einfache To-Do-Listen-Anwendung erstellen. Von nun an greift ein Benutzer auf die Anwendung zu und wird zur Authentifizierung umgeleitet.

SAML-Authentifizierungs-Benutzerfluss:

  1. Der Benutzer greift auf die ToDo-Listenanwendung des Dienstanbieters (SP) zu.
  2. Die Anwendung leitet den Benutzer zum Anmeldebildschirm von Keycloak weiter. Während dieser Weiterleitung sendet die Anwendung eine AuthnRequest an Keycloak IDP.
  3. Keycloak IDP validiert die Anfrage, wenn sie vom richtigen Relying Party/Service Provider kommt. Es prüft auf Aussteller- und Umleitungs-URI (ACS-URL).
  4. Keycloak IDP sendet eine SAML-Antwort zurück an den Dienstanbieter.
  5. Der Dienstanbieter validiert die signierte Antwort mit dem bereitgestellten öffentlichen IDP-Zertifikat.
  6. Wenn die Antwort gültig ist, extrahieren wir das Attribut NameID aus der Assertion und melden den Benutzer an.

HinweisDie SAML-Erweiterung von Spring Security war eine Bibliothek, die früher SAML-Unterstützung bot.
Aber nach 2018 hat das Spring Security-Team dieses Projekt verschoben und unterstützt jetzt SAML
2-Authentifizierung als Teil der zentralen Spring Security .

Spring Security mit SAML-Protokollbindung verwenden

Daher müssen wir nach dem Erstellen eines Spring Boot-Projekts die folgenden Abhängigkeiten importieren.


dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	/*
	 * Spring Security
	 */
	implementation 'org.springframework.boot:spring-boot-starter-security'
	runtimeOnly 'mysql:mysql-connector-java'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	implementation 'org.springframework.security:spring-security-saml2-service-provider:5.3.5' +
			'.RELEASE'

	/*
	 * Keycloak
	 */
	implementation 'org.keycloak:keycloak-spring-boot-starter:11.0.3'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

Dementsprechend ist die Abhängigkeit spring-security-saml2-service-provider ermöglicht es uns, die Registrierung der vertrauenden Seite hinzuzufügen. Es hilft auch bei der Registrierung des Identitätsanbieters.

Jetzt werden wir diese Registrierung in unserem SecurityConfig hinzufügen wie unten:


    @Bean
    public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() throws CertificateException
    {
        final String idpEntityId = "http://localhost:8180/auth/realms/ToDoListSAMLApp";
        final String webSSOEndpoint = "http://localhost:8180/auth/realms/ToDoListSAMLApp/protocol/saml";
        final String registrationId = "keycloak";
        final String localEntityIdTemplate = "{baseUrl}/saml2/service-provider-metadata" +
                "/{registrationId}";
        final String acsUrlTemplate = "{baseUrl}/login/saml2/sso/{registrationId}";


        Saml2X509Credential idpVerificationCertificate;
        try (InputStream pub = new ClassPathResource("credentials/idp.cer").getInputStream())
        {
            X509Certificate c = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(pub);
            idpVerificationCertificate = new Saml2X509Credential(c, VERIFICATION);
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }

        RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration
                .withRegistrationId(registrationId)
                .providerDetails(config -> config.entityId(idpEntityId))
                .providerDetails(config -> config.webSsoUrl(webSSOEndpoint))
                .providerDetails(config -> config.signAuthNRequest(false))
                .credentials(c -> c.add(idpVerificationCertificate))
                .assertionConsumerServiceUrlTemplate(acsUrlTemplate)
                .build();

        return new InMemoryRelyingPartyRegistrationRepository(relyingPartyRegistration);
    }

Auch unser Login ändert sich mit HttpSecurity wie folgt:

httpSecurity.authorizeRequests()
.antMatchers("/js/**","/css/**","/img/**").permitAll()
.antMatchers("/signup","/forgotpassword").permitAll()
.antMatchers("/saml/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.saml2Login(Customizer.withDefaults()).exceptionHandling(exception ->
exception.authenticationEntryPoint(entryPoint()))
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler)
.deleteCookies("JSESSIONID")
.permitAll();

Wir verwenden jetzt saml2Login . Wenn Sie auf die Anwendung zugreifen, wird sie standardmäßig zu einem Identitätsanbieter umgeleitet. Wir möchten unsere benutzerdefinierte Anmeldeseite konfigurieren, bevor sie zum Identitätsanbieter – keycloak – umgeleitet werden kann. Deshalb haben wir authenticationEntryPoint wodurch wir unsere benutzerdefinierte Anmeldeseite konfigurieren können. Wenn wir also jetzt auf unsere Anwendung unter https://localhost:8743/login zugreifen , sehen wir die folgende Anmeldeseite:

Wählen Sie also einmal die Option für Login with Keycloak SAML , wird ein AuthnRequest gesendet zu Keycloak. Außerdem ist diese Anfrage eine unsignierte Anfrage. Keycloak sendet eine signierte Antwort. Ein Controller erhält diese signierte Antwort, um NameId zu dekodieren Attribut.


@GetMapping(value="/index")
public String getHomePage(Model model, @AuthenticationPrincipal Saml2AuthenticatedPrincipal saml2AuthenticatedPrincipal)
{
   String principal = saml2AuthenticatedPrincipal.getName();
   model.addAttribute("username", principal);
   return "index";
}

Einmal die NameId abgerufen wird, wird der Benutzer angemeldet.

Konfiguration auf Keycloak

Wir müssen unsere Anwendung in der Keycloak-Administrationskonsole konfigurieren.

  • Erstellen Sie einen REALM für Ihre Anwendung.
  • Endpunkte auswählen – SAML 2.0 IdP-Metadaten
  • In Clients – Dienstanbieter hinzufügen.
  • Konfigurieren Sie für Ihren Client die Stamm-URL, die SAML-Verarbeitungs-URL (https://localhost:8743/saml2/service-provider-metadata/keycloak)
  • Sie können auch die anderen Einstellungen anpassen, z. B. – Signing Assertions, einschließlich AuthnStatement.
  • Konfigurieren Sie auf jeden Fall die ACS-URL im Abschnitt „Fine Grain SAML Endpoint Configuration“.

Code-Repository

Der Code für dieses Projekt ist in meinem Github-Repository verfügbar. Ich habe dies auch ausführlicher in meinem Buch Simplifying Spring Security behandelt. Um mehr zu erfahren, können Sie mein Buch hier kaufen.

Schlussfolgerung

In diesem Beitrag habe ich gezeigt, wie man Spring Security mit dem SAML-Protokoll verwendet. Im Laufe der Jahre gab es viele Verbesserungen in Spring Security und jetzt kann es problemlos mit verschiedenen Protokollen wie OAuth, OIDC verwendet werden. Wenn Ihnen dieser Beitrag gefallen hat, abonnieren Sie hier meinen Blog.


Java-Tag