Java >> Java tutorial >  >> Tag >> Spring

Sådan bruges Spring Security med SAML Protocol Binding

I dette indlæg vil jeg vise, hvordan vi kan bruge Spring Security med SAML Protocol Binding til at integrere med Keycloak Identity Provider. Og hvis du vil læse om, hvordan du bruger Keycloak, kan du læse her.

Hvad er SAML?

SAML står for Security Assertion Markup Language. Det er en åben standard for
udveksling af godkendelses- og autorisationsdata mellem en tjenesteudbyder (SP) og identitetsudbyder (IdP).

Identitetsudbyder – udfører godkendelse og validerer brugeridentitet til autorisation og videregiver det til tjenesteudbyderen.

Tjenesteudbyder – Stoler på identitetsudbyderen og giver brugeren adgang til service baseret på autorisation.

SAML-godkendelsesflow

Som en del af dette flow vil vi bygge en simpel opgavelisteapplikation. Fremover vil en bruger få adgang til applikationen, og han vil blive omdirigeret til godkendelse.

SAML-godkendelsesbrugerflow:

  1. Brugeren får adgang til Service Provider (SP) ToDo List Application.
  2. Applikationen omdirigerer brugeren til Keycloak-loginskærmen. Under denne omdirigering sender applikationen en AuthnRequest til Keycloak IDP.
  3. Keycloak IDP validerer anmodningen, hvis den kommer fra den rigtige pålidelige part/tjenesteudbyder. Den søger efter udsteder og omdirigerings-URI (ACS URL).
  4. Keycloak IDP sender et SAML-svar tilbage til tjenesteudbyderen.
  5. Tjenesteudbyderen validerer det signerede svar med et offentligt IDP-certifikat.
  6. Hvis svaret er gyldigt, udtrækker vi attributten NameID fra påstanden og logger brugeren ind.

BemærkSpring Security SAML-udvidelsen var et bibliotek, der plejede at levere SAML-understøttelse.
Men efter 2018 flyttede Spring Security-teamet det projekt og understøtter nu SAML
2-godkendelse som en del af kernesikkerheden for Spring .

Brug Spring Security med SAML Protocol Binding

Derfor, når du har oprettet et Spring Boot-projekt, bliver vi nødt til at importere følgende afhængigheder.


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'
	}
}

Derfor er afhængigheden spring-security-saml2-service-provider giver os mulighed for at tilføje registrering af afhængige parter. Det hjælper også med registrering af identitetsudbyder.

Nu tilføjer vi denne registrering i voresSecurityConfig som nedenfor:


    @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);
    }

Vores login ændres også med HttpSecurity som følger:

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();

Vi bruger nu saml2Login . Som standard, hvis du får adgang til applikationen, omdirigeres den til en identitetsudbyder. Vi ønsker at konfigurere vores brugerdefinerede login-side, før den kan omdirigeres til identitetsudbyderen – keycloak. Det er derfor, vi har authenticationEntryPoint som giver os mulighed for at konfigurere vores brugerdefinerede login-side. Så nu hvis vi får adgang til vores applikation på https://localhost:8743/login , vil vi se nedenstående login-side:

Så når du vælger muligheden for Login with Keycloak SAML , vil den sende en AuthnRequest til Keycloak. Denne anmodning er også en usigneret anmodning. Keycloak sender et underskrevet svar. En controller vil modtage dette signerede svar for at afkode NameId attribut.


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

Når NameId er hentet, vil det logge brugeren ind.

Konfiguration på Keycloak

Vi bliver nødt til at konfigurere vores applikation i Keycloak-administrationskonsollen.

  • Opret en REALM til din applikation.
  • Vælg endepunkter – SAML 2.0 IdP-metadata
  • I klienter – Tilføj tjenesteudbyderen.
  • For din klient skal du konfigurere rod-URL, SAML-behandlings-URL (https://localhost:8743/saml2/service-provider-metadata/keycloak)
  • Du kan også justere de andre indstillinger som – signering af påstande, inklusive AuthnStatement.
  • Konfigurer bestemt ACS URL'en i afsnittet "Fine Grain SAML Endpoint Configuration".

Kodelager

Koden til dette projekt er tilgængelig i mit github-lager. Jeg dækkede også dette mere detaljeret i min bog Simplifying Spring Security. Hvis du vil vide mere, kan du købe min bog her.

Konklusion

I dette indlæg viste jeg, hvordan man bruger Spring Security med SAML-protokol. Gennem årene har der været en masse forbedringer i Spring Security, og nu kan det nemt bruges med forskellige protokoller som OAuth, OIDC. Hvis du kunne lide dette indlæg, så abonner på min blog her.


Java tag