Spring Security, SAML & ADFS: Implementace
Posledně jsme se vyřádili na konfiguraci, tak teď už jen zbývá to nabouchat v tom Springu, ne? Dobrá zpráva je, že pokud budete následovat Reference Documentation, bude vám Spring SAML a ADFS fungovat out-of-the-box.
Špatná zpráva je, že pokud budete chtít použít Java configuration, nemáte se moc kde inspirovat. Pokud vím, tak k dnešnímu dni jsou k dispozici jen dva příklady:
- vdenotaris/spring-boot-security-saml-sample a speciálně jeho WebSecurityConfig
- a potom můj skromný příspěvek sw-samuraj/blog-spring-security, který je předmětem tohoto článku.
Dalším benefitem mého příspěvku a ukázkového projektu je, že používají aktuální verzi Spring Frameworku a Spring Security, tedy verzi 5 (v tomhle asi budu chviličku unikátní). Třešničkou na dortu je pak buildování pomocí Gradle (protože kdo by ještě chtěl v dnešní době používat Maven, že jo? 😜
Závislosti
Pro zdar operace budeme potřebovat následující závislosti:
Drobná Gradle poznámka: Protože používám současnou verzi Gradlu, používám konfiguraci implementation
. Pro starší verze Gradle (2.14.1-) použijte původní (nyní deprecated) konfiguraci compile
.
Spring SAML Configuration
Ať už se použije XML, nebo Java konfigurace, bude to v každém případě velmi dlouhý soubor. Velmi. I když nebudu počítat téměř 40 řádek importů, i tak zabere ta nejzákladnější konfigurace zhruba 5 obrazovek. Víc se mi to ořezat nepodařilo.
Ale nebojte se, nebudu vás oblažovat každým detailem. Jen vypíchnu to zajímavé, vynechám co jsem zmiňoval v minulém díle o konfiguraci a pro zbytek konfigurace vás odkážu do svého repozitáře, kde si to můžete vychutnat celé: SecurityConfiguration.java.
Nastavení HttpSecurity
Nebudu příliš zabíhat do podrobností, jak funguje samotné Spring Security (prostě chrání pomocí filtrů určité URL/zdroje) a podívám se na jedno konkrétní nastavení:
- Vypnuté CSRF. U SAMLu nedává CSRF moc smysl — SAML requesty jsou podepsané privátním klíčem daného SP, jehož veřejný klíč je zaregistrován na použitém IdP.
- Přídání dvou filtrů: jeden pro SAML metadata (
metadataGeneratorFilter
), druhý řeší samotný SAML mechanismus (samlFilter
). - Definice URL, které vyžadují autentikaci (
/user
). - Podstrčení SAML entry pointu namísto přihlašovacího formuláře (
loginPage("/saml/login")
). - Přesměrování na root kontext aplikace po úspěšném odhlášení (
logoutSuccessUrl("/")
).
SAML filtry
Základem jak Spring Security, tak Spring Security SAMLu jsou filtry — odchytí HTTP(S) komunikaci a transparentně aplikují zabezpečení aplikace. V případě SAMLu je těch filtrů celá smečka, ale v zásadě řeší jen tři věci: přihlášení (SSO), odhlášení (SLO) a metadata. Čtvrtým mušketýrem může být ještě IdP discovery, ale tu v našem případě nemáme.
Key manager
Všechny SAML zprávy, jež si IdP a SP vyměňují jsou podepsané privátním klíčem dané strany. Doporučuji mít pro SAML podpisový klíč separátní key store (nemíchat ho třeba s key storem, který potřebuje aplikační server pro HTTPS).
V naší ukázkové aplikaci je SAML key store na classpath — v jakémkoli jiném, než lokálním vývojovém prostředí, key store samozřejmě externalizujeme (nepřibalujeme do WARu) a hesla kryptujeme.
Podepisování SHA-256
V minulém díle jsem zmiňoval, že Spring SAML defaultně používá při podepisování algoritmus SHA-1, kdežto ADFS očekává SHA-256. Jedna strana se musí přizpůsobit. Doporučuji upravit aplikaci — použít SHA-256 není nic těžkého.
Výběr podpisového algoritmu se provádí při inicializaci SAMLu pomocí třídy SAMLBootstrap, která bohužel není konfigurovatelná. Pomůžeme si tak, že od třídy podědíme a potřebný algoritmus podstrčíme:
V konfiguraci pak třídu instancujeme následujícím způsobem. Mimochodem, povšimněte si, že beana je instancovaná jako static
. To proto, aby inicializace proběhal velmi záhy při vytváření kontextu.
That’s All Folks!
Tím se náš 3-dílný mini seriál o Spring Security, SAMLu a ADFS uzavírá. Samozřejmě, že bych mohl napsat ještě mnoho odstavců a nasdílet spoustu dalších gistů. Ale už by to bylo jen nošení housek do krámu.
Lepší bude, pokud si teď stáhnete ukázkový projekt sw-samuraj/blog-spring-security, trochu se povrtáte ve zdrojácích a na závěr v něm vyměníte soubor FederationMetadata.xml
a zkusíte ho rozchodit vůči vašemu ADFS. Při troše štěstí by to mělo fungovat na první dobrou 🙂
Jako bonus pro odvážné — pokud se opravdu pustíte do těch zdrojových kódů — můžete v historii projektu najít další Spring Security ukázky (je to celkem rozumně otagovaný):
- Výměna CSRF tokenu mezi Springem a Wicketem (tag
local-ldap
). - Multiple HttpSecurity — v jedné aplikaci: autentikace uživatele přes formulář a mutual-autentication REST služeb přes certifikát (tag
form-login
). - Autentikace vůči lokálnímu (embedovanému) LDAPu (tag
local-ldap
). - Autentikace vůči Active Directory (tag
remote-ad
).