自建SAML IDP可行性、实现方案及Java适配技术咨询
Hey there! Let's tackle your questions about building a custom SAML IDP—this is a topic I’ve helped several teams work through, so let’s break it down clearly:
Absolutely, but only in specific scenarios. Here’s when it makes sense:
- Highly customized identity workflows: If your system has unique auth logic (like custom multi-factor authentication flows, deep integration with legacy internal systems) that off-the-shelf IDPs (Okta, Auth0, Azure AD) can’t accommodate without heavy workarounds.
- Strict compliance/privacy requirements: Industries like finance or healthcare often mandate that user identity data stays on-premises or within specific geographic regions—third-party IDPs might not meet these data residency rules.
- Full control over auth pipeline: If you need to own every step of the authentication process (from user validation to assertion generation) and avoid relying on third-party service uptime or unexpected feature changes.
That said, don’t build a custom IDP if you just need basic SSO. The maintenance burden (security patches, compliance audits, spec updates) is massive, and mature third-party tools will save you months of work.
If you’ve decided to move forward, follow this structured approach:
- Step 1: Learn the SAML 2.0 core specs
You need to understand key concepts: AuthnRequests, SAML Responses, Assertions (Authn, Attribute, Authz), Metadata, and bindings (HTTP-Redirect, HTTP-POST). Focus on how signatures and encryption work—this is where most security gaps happen. - Step 2: Define core IDP capabilities
- User authentication module: Integrate with your existing user store (LDAP, internal user API, database) to validate credentials and support MFA if needed.
- AuthnRequest handling: Receive and validate incoming requests from SPs (check signatures, verify ACS URLs match registered metadata).
- Assertion generation: Create SAML assertions with user attributes (email, user ID, roles), set valid expiration windows (
NotBefore/NotOnOrAfter), and target the correct SP audience. - Signature & encryption: Sign responses/assertions with your private key; encrypt assertions if the SP requires it (using their public key).
- Metadata management: Generate and host your IDP’s metadata XML (includes entity ID, SSO endpoint, public keys) for SPs to consume. Also support importing SP metadata to auto-configure their settings.
- Step 3: Test rigorously with sample SPs
Use tools like SimpleSAMLphp (as an SP) or Spring Security SAML (as an SP) to end-to-end test the flow: SP initiates auth → IDP authenticates user → IDP sends response → SP validates assertion. - Step 4: Harden for security & compliance
- Rotate signing/encryption keys regularly and store them in a secure vault (HSM, cloud KMS).
- Log all auth requests, responses, and errors for audit trails.
- Guard against common SAML attacks: assertion replay, XML injection, signature bypass (follow OWASP’s SAML security guidelines).
You’re right that the older Spring Security SAML library was SP-only, but the latest Spring Security SAML 2.0 (included in Spring Security 5.6+) fully supports IDP functionality—this is a common misconception. Here’s how to use it:
Option 1: Spring Security SAML 2.0 (IDP Mode)
You can configure it to handle AuthnRequests, authenticate users, and generate signed SAML responses. A quick example:
@Configuration public class Saml2IdpConfig { @Bean public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { // Register your SP's details (from their metadata) RelyingPartyRegistration spRegistration = RelyingPartyRegistration.withRegistrationId("my-sp") .entityId("https://my-sp.com/saml/metadata") .assertionConsumerServiceLocation("https://my-sp.com/saml/acs") .signingX509Credentials(c -> c.add(Saml2X509Credential.signing(getSigningKey()))) .build(); return new InMemoryRelyingPartyRegistrationRepository(spRegistration); } @Bean public Saml2IdpFilter saml2IdpFilter(RelyingPartyRegistrationRepository repo, AuthenticationManager authManager) { Saml2IdpFilter filter = new Saml2IdpFilter(repo, authManager); filter.setAuthenticationConverter(new Saml2AuthenticationRequestConverter()); filter.setAuthenticationSuccessHandler((request, response, auth) -> { // Generate and send the SAML response to the SP RelyingPartyRegistration reg = repo.findByRegistrationId("my-sp"); Saml2Response samlResponse = Saml2Response.withRelyingPartyRegistration(reg) .authentication(auth) .relayState(request.getParameter("RelayState")) .signingCredential(getSigningKey()) .build(); // Send via HTTP-POST Saml2ResponseSender.send(samlResponse, response); }); return filter; } private Saml2X509Credential getSigningKey() { // Load your signing key from a keystore or vault KeyStoreKeyFactory keyFactory = new KeyStoreKeyFactory(new ClassPathResource("idp-keystore.jks"), "keystore-password".toCharArray()); PrivateKey privateKey = keyFactory.getKey("idp-signing-key", "key-password".toCharArray()); X509Certificate cert = keyFactory.getCertificate("idp-signing-key"); return Saml2X509Credential.signing(privateKey, cert); } }
Option 2: OpenSAML (Full Flexibility)
If you need more control than Spring Security provides, OpenSAML is the de facto standard Java library for SAML. It’s low-level, but supports every part of the SAML 2.0 spec. Example of generating an assertion:
// Initialize OpenSAML (run once at app startup) DefaultBootstrap.bootstrap(); // Create an AuthnStatement AuthnStatement authnStatement = AuthnStatementBuilder.buildAuthnStatement( Instant.now(), AuthnContextClassRefBuilder.buildAuthnContextClassRef(AuthnContext.PASSWORD_AUTHN_CTX) ); // Create the assertion with user attributes Assertion assertion = AssertionBuilder.buildAssertion( "https://my-idp.com/saml/entity-id", // IDP entity ID "https://my-sp.com/saml/entity-id", // SP audience Instant.now().minusSeconds(60), // NotBefore Instant.now().plusMinutes(10), // NotOnOrAfter authnStatement, List.of( AttributeBuilder.buildAttribute("email", "user@example.com"), AttributeBuilder.buildAttribute("user_id", "12345") ) ); // Sign the assertion Signature signature = SignatureBuilder.buildSignature(getSigningCredential()); assertion.setSignature(signature); // Marshall to XML string Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(assertion); marshaller.marshall(assertion); String assertionXml = XMLHelper.nodeToString(assertion.getDOM());
Note: OpenSAML has a steep learning curve, but it’s what powers tools like Shibboleth.
Option 3: Customize Shibboleth IDP
If you don’t want to build from scratch, Shibboleth is an open-source SAML IDP built on OpenSAML. You can extend its modules to integrate with your user store, customize attribute release, or add custom auth flows—this saves you from handling low-level SAML logic.
内容的提问来源于stack exchange,提问作者Praveen Kamath

