Spring Security is a feature rich framework for handling security concerns in a web application. As standard, it has little support for SAML. However, SAML is now supported as an extension project – Spring Security SAML.
SAML
SAML (Security Assertion Markup Language) is an open standard that supports federated user login. That is, a user may authenticate to an Identity Provider (IdP) and then access an independent Service Provider (SP) without having to re-establish their identity. In practice, this usually means that a user provides their username and password to an application on one domain (the IdP) and can then single sign on (SSO) to the to an application on a different domain (the SP) without having to re-enter the username and password. Crucially, the SP is never even aware of the user’s password. So long as the SP trusts the IdP and the IdP trusts the user then the SP can trust the user too. SAML is the data format that allows this trust to be established and the user’s identity to be securely established on the SP.
Sample app
Spring Security SAML comes with an excellent sample app which can be set up in just a few minutes. After downloading the package from spring-security-saml on GitHub, the sample app can be run just by modifying a couple of files and deploying to Tomcat. The quick start guide will get you as far as setting up the sample app to SSO with SSO Circle, a public free identity provider. In this case, the sample app serves as the SP and SSO Circle serves as the IdP.
When the sample app is running, it serves not only to demonstrate the code, but to assist with generation of metadata required by the Identity Provider (IdP). This is a necessary step to establish the trust relationship between the SP and the IdP.
Building into an existing app
Building Spring Security SAML into an existing Spring Security application is also fairly straightfoward. As a demonstration, I’ve added it to the legendary Spanners demo app. Download version 2.5 to see this in action.
For the most part, the spring-security-context.xml file is just a copy of the securityContext.xml file taken from the Spring Security SAML sample app. I’ve made a few changes to configure SAML the way I want and to configure the Spanners app security correctly.
IdP Discovery and selection
The SAMLDiscovery bean is responsible for choosing one of the configured IdPs to log in against:
<!-- IDP Discovery Service --> <bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery"> <property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/> </bean>
The idpSelectionPath property defines a page that lets the user choose which IdP to login against. The Spanners demo app federates against Circle SSO only so there’s no point in showing this page. The SAMLDiscovery bean will automatically return the default IdP if no idpSelectionPath property is set:
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery"> <!-- Do not show the IdP selection page. Always use the default IdP. There's only one configured anyway. --> <!--<property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/> --> </bean>
SAMLUserDetailsService
The SAMLUserDetailsService is similar to the Spring Security UserDetailsService interface. Annoyingly though, it’s a separate interface, not a sub interface. This means that any implementation of UserDetailsService that you already have will have to be reimplemented for SAML (I’m sure that a wrapper implementation of SAMLUserDetailsService that bridges to a UserDetailsService wouldn’t be too hard to make).
The SAMLUserDetailsService is optional. If it’s not provided, you’ll get an instance of OpenSAML NameIDImpl as your principal. This is a little fiddly to work with and is likely to cause cause issues if you’re converting an existing Spring Security project. Spring Security usually uses an implementation of UserDetails as the principal.
I’d recommend creating an implementation of SAMLUserDetailsService that returns a UserDetails object. I created a trivial implementation that grants every logged in user a standard set of roles:
public class SimpleSAMLUserDetailsService implements SAMLUserDetailsService { public static final String DUMMY_PASSWORD = "DUMMY_PASSWORD"; private List<String> roles; public void setRoles(List<String> roles) { this.roles = roles; } public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException { String username = credential.getNameID().getValue(); Collection<GrantedAuthority> gas = new ArrayList<GrantedAuthority>(); for (String role : roles) { gas.add(new SimpleGrantedAuthority(role)); } return new User(username, DUMMY_PASSWORD, gas); } }
IdP / Metadata
The easiest way to configure the necessary metadata to establish trust with an IdP is to use the metadata display screen in the Spring Security SAML sample app. You could of course build these features into your own app too but you may not want to include this feature in a production application. For the Spanners app, I used the SAML sample app to set up all required metadata and then just copied the configuration for Spanners. I’ve removed the metadata display filter from the final application.
Security Annotations
Finally, I’ve configured method level security annotations as described in Protecting Service Methods with Spring Security Annotations.
Project status
At time of writing, this project is still sitting at RC (Release Candidate) status and it’s been over a year since the last RC release. However, there is still recent activity on the project and a recent forum post indicates that a GA (General Availability) release looks imminent.