Spring Security, SAML & ADFS: Konfigurace

Minule jsme se podívali — z obecnějšího pohledu — jak SAML funguje pro autentikaci aplikace. Kromě toho, že byste měli znovu zkouknout ty pěkné barevné diagramy, zobrazující SSO (Single Sign-On) a SLO (Single Logout), by se vám mohl hodit SAML a ADFS slovníček — od teď už očekávám, že termíny máte našprtané ;-)
Tenhle článek se bude zaměřovat na konfiguraci potřebnou pro to, aby nám SAML autentikace fungovala. V realitě pak tato konfigurace půjde nejčastěji ruku v ruce s implementací, protože abyste získali SP (Service Provider) metadata, budete potřebovat ho mít už funkční (pokud nejste SAML-Superman, který to zvládne nabouchat ručně, nebo externím nástrojem).
Registrace metadat
Aby nám SAML fungoval, musíme nejdřív vzájemně zaregistrovat jednotlivé IdP (Identity Providery) a SP (Service Providery). Jak jsme si říkali v minlém díle, vztah IdP — SP může být M:N, nicméně pro zbytek článku (a i v tom následujícím) budeme uvažovat jenom vztah 1:1, tedy náš SP (aplikace) se autentikuje vůči jednomu IdP.
Registrace metadat se může provést dvojím způsobem — buď můžeme poskytnou URL, na kterém si IdP/SP metadata sám stáhne při svém startu, nebo (asi častější) metadata poskytneme jako statický soubor. Zde budeme pracovat s druhým případem.
Registrace (ADFS) Federation metadat
Registrace Federation Metadat je jednoduchá — stáhneme XML soubor z daného URL a protože pro implementaci SP používáme Spring Security SAML, poskytneme ho jako resource pro MetadataProvider.
Metadata na ADFS serveru najdeme na následujícím URL:
Federation Metadata je dlouhý, ošklivý XML soubor. Způsob, jak ho poskytnout naší aplikaci je trojí:
- dát ho na classpath a načíst třídou
ClasspathResource
- dát ho na file systém a načíst třídou
FilesystemResource
- načíst ho přímo z ADFS třídou
HttpResource
Pokud např. soubor FederationMetadata.xml
umístíme do adresáře src/main/resource/metadata
, můžeme ho načíst následujícím způsobem:
Pokud potřebujete trochu víc (Spring) kontextu, může se podívat do kompletní Spring SAML konfigurace:
Registrace (Spring) SAML metadat
Registraci SAML metadat na ADFS si rozdělíme do dvou kroků:
- Získání metadat z aplikace.
- Registraci metadat na ADFS.
Generování SAML metadat (z aplikace)
Tady přichází ke slovu, co jsem předesílal — abyste byli schopný si vygenerovat SAML metadata, budete potřebovat mít Spring SAML aspoň částečně naimplementovaný. O generování metadat se stará Springovský filtr MetadataGeneratorFilter.
Generování metadat out-of-the-box funguje dobře (a s ADFS ho rozchodíte). Pokud chcete, nebo musíte metadata upravit, tohle je to správné místo. Například specifické (SP) Entity ID se dá nastavit tímto způsobem:
Filter pak necháme naslouchat na určitém URL endpointu, kde nám bude metadata poslušně generovat:
Situace je samozřejmě trochu složitější, takže pokud jste netrpělivý, nebo vám chybí potřebné Spring beany, nahlídněte do zmiňované SecurityConfiguration.java.
Ještě než si metadata vygenerujete a stáhnete z daného URL, jedno důležité upozornění! Při nesplnění následujících podmínek vám SAML před ADFS nebude fungovat.
- Na URL musíte přistoupit přes HTTPS (a mít tedy odpovídajícím způsobem nakonfigurovaný aplikační server/servlet kontejner).
- Na URL musíte přistoupit přes hostname nebo IP adresu, které jsou z IdP (ADFS) viditelné. (Takže ne https://localhost.)
Registrace metadat na ADFS
Teď se magicky přeneseme na ADFS server (typicky přes Remote Desktop), kam si zkopírujeme vygeneraovaný SAML metadata soubor. Spring ho defaultně nazývá spring_saml_metadata.xml
, nicméně na jméně nezáleží.
V AD FS Management konzoli nás bude zajímat jediná věc — položka Relying Party Trust, kde metadata našeho SP zaregistrujeme, resp. naimportujeme.

AD FS Management konzole
Import metadat se provádí pomocí wizardu (jak jinak ve Windows). Je to jednoduché a přímočaré: zadáme import ze souboru a pak už se jen doklikáme nakonec:

Import SP metadat ze souboru
V závěrečném shrnutí je dobré si zkontrolovat, že v sekci Endpoints jsou splněny dvě výše uvedené podmínky (HTTPS a hostname/IP adresa):

Kontrolní shrnutí SP endpointů
ADFS defaultně očekává, že hash algoritmus použitý při podepisování SAML zpráv bude SHA-256. Bohužel, Spring SAML posílá out-of-the-box SHA-1. Máte tedy dvě možnosti:
- Upravit Spring SAML, aby používal SHA-256 (jak to ohackovat vám prozradím příště),
- nebo říct ADFS, aby očekávalo SHA-1.
Nastavení Secure hash algorithm najdete po rozkliknutí Properties na záložce Advanced (je potřeba to udělat dodatečně — při importu metadat je tato volba nefunkční):

Nastavení Secure hash algorithm
Pokud si to na obou stranách nesladíte, budete na straně Springu dostávat nic neříkající výjimku:
2018-01-17 12:15:01 DEBUG org.springframework.security.saml.SAMLProcessingFilter --- Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100) ~[spring-security-saml2-core-1.0.3.RELEASE.jar:1.0.3.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.0.0.RELEASE.jar:5.0.0.RELEASE]
Caused by: org.opensaml.common.SAMLException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:113) ~[spring-security-saml2-core-1.0.3.RELEASE.jar:1.0.3.RELEASE]
at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87) ~[spring-security-saml2-core-1.0.3.RELEASE.jar:1.0.3.RELEASE]
A na straně ADFS nápomocnější:
Microsoft.IdentityServer.Protocols.Saml.SamlProtocolSignatureAlgorithmMismatchException:
MSIS7093: The message is not signed with expected signature algorithm.
Message is signed with signature algorithm http://www.w3.org/2000/09/xmldsig#rsa-sha1.
Expected signature algorithm http://www.w3.org/2001/04/xmldsig-more#rsa-sha256.
Takže, prozatím SHA-1. “Dvě stě padesát šestka” bude příště ;-)
Claim Rules
Poslední věc, kterou zbývá nastavit je mapování claims na assertions. Na naší nově vytvořené Relying Party Trust dáme Edit Claim Rules… a přidáme následující pravidlo, které nám z Active Directory vytáhne Name ID a doménové skupiny daného uživatele:

Editace Claim Rules
To be continued…
Tak a máme hotovo! Teda konfiguraci. Ovšem, jak už jsem naznačoval, v tento moment už stejně většinou máte hotovou i zbývající implementaci, takže pokud jsme nic neopomněli, mělo by nám fungovat jak SSO, tak SLO, přesně podlě těch krásných diagramů z minulého dílu.
V příštím, závěrečném díle, se podíváme, jak nabastlit zbytek Springovských věcí — můžete se těšit na Java konfiguraci (což je zatím vzácnost, protože Reference Documentation stále jede na XML) a samozřejmě pofrčíme na aktuálním Spring 5 (Cože?!? Vy jedete ještě na čtyřce?! No, nekecej 8-)