Integrating JSF web application with Openam using Spring Saml Extension.
Or Single Sign on With JSF
Or Java Single Sign on
Or Integrate Single sign on Using Spring SAML Extension.
As discussed in my earlier article we can have an different set ups
Case1: SP (Your Java Web app) <===> IDP Proxy <=====> (IDP1, IDP2.....)
or if you just have one IDP then obviously there is no point in using IDP proxy in which case you setup will be like
Case2: SP (Your Java Web app or Openam generated Fedlet) <=====> (IDP)
So depending on CASE1 or CASE2 you will generate Fedlet on IDP Proxy or IDP respectively.
So What is Fedlet ?
- Well Fedlet is small web based openam client that can be generated once you install openam. This web application will have few jsps and will help you to test your openam setup and it has enough code to send SAML requests and receive SAML Responses
Now lets get in to details as how to plugin in your JSF webapp with Openam ?
1. Lets say you have JSF webapp with a simple page with following url
http://abc.com:8080/TST/landingPage.jsf
STEP1: Login to openam (IDP Proxy if its CASE1 else IDP if its CASE2) and click on generate the Fedlet. When you generate the Fedlet choose your
STEP2: Now in fedlet war there is a "conf" directory with 4 xmls
i.e
Copy content from idp.xml starting from to sp.xml.
<KeyDescriptor use="signing">
STEP3: We can use the idp.xml that’s found in Fedlet/conf directory. This is the xml that
will allow our JSF application to send SAML request to Openam.
STEP4:. We can use the sp.xml that’s found in the Fedlet/conf directory. This will have information for service provider (TST JSF application ).There are few changes we need to make here in order for the response url’s to be intercepted by Spring SAML extension filters.
e.g. I generated Fedlet for entity id http://abc.com:8080/TST and here is how the changes look in sp.xml.
STEP5: Change AssertionConsumerService (ACS) URL’S in sp.xml
FROM:
Here the realm I used for testing was "realm001" in case you have used root realm then your url will be saml/SSO/alias/sp
STEP6:
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</NameIDFormat>
-->
STEP7: In case you want your requests signed and assertions signed in sp.xml set
I had authentication turned and use a sample cert. For testing you can use the test cert provided by openam . Never use this "test" cert in a production environment.
STEP10:
Now in openam IDP (If you ar using proxy go to proxy) Under Federation > entity providers > your SP (ie. http://abc.com:8080/TST) and you should see signing and encryption with value of the certs.
NOTE:This note is applicable only if you are using openam as a proxy. IGNORE THIS is you are using openam as IDP.
Login in to Openam Proxy >Go to Federation > Entity Providers (http://abc.com:8080/TST) -- Basically the remote Service provider )> SP> Advanced > IDP Proxy and make sure proxy count is set to 1 (i.e number one) and proxy url is set to your IDP url (yes the proxy url is idp url).The proxy uses this value to send request to IDP.
STEP11:
Verify that the acs url you have added in sp.xml is present in openam.
Login to IDP ( if you are using proxy login into openam proxy)> Click on Federation tab
Go to Entity providers section and click on the URL that we used to generate Fedlet (In this case we used http://abc.com:8080/TST )
While on SP tab go to “Services” screen scroll down to the section which has“Assertion consumer Service”
From: http://abc.com:8080/TST/fedletapplication
To: http://abc.com:8080/TST/saml/SSO/alias/realm001/sp
NOTE: I did not use single logout service. In case you want change the logout URL’s on this page. (Locally I changed all logout urls to “TST/saml/SingleLogout/alias/realm001/sp”)
Congratulations !!!! Your configuration to plugin SP with openam is complete.
We now need to focus on Spring code changes on you JSF web app.
Spring SAML extension integration with your
JSF application
or spring security saml tutorial
or spring security saml tutorial
or spring security jsf example
Here I will brief you about how to plugin spring code with your JSF application.
I always had a question as why spring SAML uses the filters with urls patterns predefined.
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
//We dont need metadata to be displayed comment this.
<!-- <security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/> -->
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
//We dont need Springdiscovery as we have one idp.So commented it.
<!-- <security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/> -->
This is how the spring filters will intercepts the urls.
Don't mess with this patterns.Thats the reason we changed the ACS and logout urls as mentioned in STEP5.
Once you have this rams-applicationContext-security.xml all it needs is the below files in your path or classpath. (See the rams-applicationContext-security.xml)
You can download the below code from
https://sites.google.com/site/reddymails/Home/SAMLUserDetailsServiceImpl.java?attredirects=0&d=1
So now you have spring security xml and the code to parse SAML repsonse ready. Just plugin. The srping xml in to your web.xml. Since the code above parsed the roles the user is we need to use those roles to enable or disable feature sin JSF pages which I will explain shortly.
In your webxml
I have attached the sample web.xml
https://sites.google.com/site/reddymails/Home/web.xml?attredirects=0&d=1
How do to we read the User name from Spring security context ?
I did some reading and here is code that saves you a day :)
The assumption is that your are sending "Username" as one of the attributes in SAML response.
Once you parse store it in Httpsession that way you don't run this code multiple times for same user.
public static final String USER_ID = "UserName";
public String getCurentUserName(){
Just follow these steps
<!-- To add Security tag tld to JSF you can read more at.
more at
http://static.springsource.org/spring-webflow/docs/2.2.x/reference/html/ch13s09.html
http://doanduyhai.wordpress.com/2012/02/26/spring-security-part-v-security-tags/
-->
Assuuming you have configured the Spring security tld now you can add name space in JSF page as shown below.
xmlns:security="http://www.springframework.org/security/tags"
And inside your JSF page now you can use tags like
rendered="#{security:areAnyGranted('FILE_READ_GROUP,FILE_EDIT_GROUP')}">
rendered="#security:areAllGranted('FILE_ALL_OPERATIONS_GROUP)"
disabled="#{security:areNotGranted('FILE_READ_GROUP)}">
Where FILE_READ_GROUP, FILE_ALL_OPERATIONS are Active directory groups the current user is in.
Hope this article take some pain of integrating openam with Spring SAML and also helps you in Spring Security integration with JSF application.
Or Single Sign on With JSF
Or Java Single Sign on
Or Integrate Single sign on Using Spring SAML Extension.
Or Integrating Spring SAML Extension with Openam (or any SAML provider).
Or Spring and Open AM security
Or Single sign on in JSF or Single Sign on with JSF
Or Spring security with openam
Or Spring and Open AM security
Or Single sign on in JSF or Single Sign on with JSF
Or Spring security with openam
As discussed in my earlier article we can have an different set ups
Case1: SP (Your Java Web app) <===> IDP Proxy <=====> (IDP1, IDP2.....)
or if you just have one IDP then obviously there is no point in using IDP proxy in which case you setup will be like
Case2: SP (Your Java Web app or Openam generated Fedlet) <=====> (IDP)
So depending on CASE1 or CASE2 you will generate Fedlet on IDP Proxy or IDP respectively.
So What is Fedlet ?
- Well Fedlet is small web based openam client that can be generated once you install openam. This web application will have few jsps and will help you to test your openam setup and it has enough code to send SAML requests and receive SAML Responses
Now lets get in to details as how to plugin in your JSF webapp with Openam ?
1. Lets say you have JSF webapp with a simple page with following url
http://abc.com:8080/TST/landingPage.jsf
STEP1: Login to openam (IDP Proxy if its CASE1 else IDP if its CASE2) and click on generate the Fedlet. When you generate the Fedlet choose your
- Realm (You can use root "/" realm, but better use different realm. I used realm001)
- Circle of Trust : Let say "Cot1"
- Identityprovider: (The url of your openam Identity provider will appear in drop down)
- Fedlet Name :http://abc.com:8080/TST (Can be anything but I just use url)
- Fedlet destination url : http://abc.com:8080/TST
STEP2: Now in fedlet war there is a "conf" directory with 4 xmls
- sp.xml
- sp-extended.xml
- idp.xml
- idp-extended.xml
i.e
Copy content from idp.xml starting from to sp.xml.
<KeyDescriptor use="signing">
……..
</KeyDescriptor>
<KeyDescriptor
use="encryption">
…….
</KeyDescriptor>
e.g. I generated Fedlet for entity id http://abc.com:8080/TST and here is how the changes look in sp.xml.
Comment the following in sp.xml
FROM:
<!-- Default Fedlet generated Log out
Urls.
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="http://abc.com:8080/TST/fedletSloRedirect"
ResponseLocation="http://abc.com:8080/TST/fedletSloRedirect" />
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://abc.com:8080/TST/fedletSloPOST"
ResponseLocation="http://abc.com:8080/TST/fedletSloPOST" />
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="http://abc.com:8080/TST/fedletSloSoap" />
-->
Change the above
Single logout service url’s to as shown below (Basically in every URL we added "/saml/SingleLogout/alias/realm001/sp" ) where realm001 is the realm and
“/sp” is the service provider url we choose when we created openam.Be extra
careful when replacing this url to have correct realm name.
In case you have used root realm then you can replace the below urls with /saml/SingleLogout/alias/sp
TO: (Within sp.xml)
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="http://abc.com:8080/TST/saml/SingleLogout/alias/realm001/sp"
ResponseLocation="http://abc.com:8080/TST/saml/SingleLogout/alias/realm001/sp" />
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://abc.com:8080/TST/saml/SingleLogout/alias/realm001/sp"
ResponseLocation="http://abc.com:8080/TST/saml/SingleLogout/alias/realm001/sp"
/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="http://abc.com:8080/TST/saml/SingleLogout/alias/realm001/sp" />
FROM:
<!-- Default Fedlet
generated
<AssertionConsumerService
isDefault="true"
index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://abc.com:8080/TST/fedletapplication"
/>
<AssertionConsumerService
index="1"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="http://abc.com:8080/TST/fedletapplication"
/>
-->
TO:
<AssertionConsumerService isDefault="true"
index="0" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://abc.com:8080/TST/saml/SSO/alias/realm001/sp" />
<AssertionConsumerService index="1"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="http://abc.com:8080/TST/saml/SSO/alias/realm001/sp" />
Here the realm I used for testing was "realm001" in case you have used root realm then your url will be saml/SSO/alias/sp
STEP6:
Please comment the following name id formats in sp.xml
and leave transient name id format.
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<!-- We don't need
this as we use just transient
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat><NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</NameIDFormat>
-->
STEP7: In case you want your requests signed and assertions signed in sp.xml set
AuthnRequestsSigned="true" WantAssertionsSigned="true"
I had authentication turned and use a sample cert. For testing you can use the test cert provided by openam . Never use this "test" cert in a production environment.
STEP8:
Edit the
sp-extended.xml by fixing metalias to include realm name and also verify the
following atttiburtes
These 3 tags will have values only if you have openam proxy. If you have just openam IDP and your SP is talking to IDP directly do no set values for these tags.
<Attribute name="enableIDPProxy">
<Value>true</Value>
</Attribute>
<Attribute name="idpProxyCount">
<Value>1</Value></Attribute>
<Attribute name="idpProxyList">
<Value>http://idp.abc.com:8080/openam</Value> //Strangely you use the IDP url though the tag says proxy
</Attribute>
//Set the cert names for signing and encryption.
<Attribute name="signingCertAlias">
<Value>test</Value> //In production you can use separate cert for signing and encryption
</Attribute>
<Attribute name="encryptionCertAlias">
<Value>test</Value> //In production you can use separate cert for signing and encryption
</Attribute>
Change All want*Encrypted properties (Only if you want everything encrypted. I did it when I tested)
<Attribute
name="wantAttributeEncrypted">
<Value>true</Value>
</Attribute>
<Attribute name="wantAssertionEncrypted">
<Value>true</Value>
</Attribute>
<Attribute name="wantNameIDEncrypted">
<Value>true</Value>
</Attribute>
<Attribute
name="wantAssertionEncrypted">
<Value>true</Value>
</Attribute>
All want*Signed properties (Only if you want everything Signed. I did it when I tested)
<Attribute name="wantPOSTResponseSigned">
<Value>true</Value>
</Attribute>
<Attribute
name="wantArtifactResponseSigned">
<Value>true</Value>
</Attribute>
<Attribute
name="wantLogoutRequestSigned">
<Value>true</Value>
</Attribute>
<Attribute
name="wantLogoutResponseSigned">
<Value>true</Value>
</Attribute>
<Attribute
name="wantMNIRequestSigned">
<Value>true</Value>
</Attribute>
<Attribute
name="wantMNIResponseSigned">
<Value>true</Value>
</Attribute>
In sp-extended.xml add realm name (In case you are using root you dont need to change anything just use the existing values)
metaAlias="/realm001/sp"
metaAlias="/realm001/attrQuery"
metaAlias="/realm001/pep"
In idp-extended.xml add realm name
metaAlias="/realm001/idp"
metaAlias="/realm001/sp"
In sp-extended.xml set
hosted="0" (This tells openam that the entity you are trying to
upload (i.e. your SP) is remote and not locally hosted on Openam itself)
STEP 9:
Since we changed assertion consumer url in the sp.xml
we need to update this url in IDP proxy, as IDP proxy will have Feldet url which we have changed to keep Spring SAML
extension happy.
2. Navigate
to Federation > entity providers.
3. Delete
the service provider . ie. Entry which we added to generate Fedlet for our JSF app URL (eg: http://abc.com:8080/TST)
4. Make
sure the same entity is also removed in Circle of Trust
5. Click
on import entity and select sp.xml and
sp-extended.xml respectively
STEP10:
Now in openam IDP (If you ar using proxy go to proxy) Under Federation > entity providers > your SP (ie. http://abc.com:8080/TST) and you should see signing and encryption with value of the certs.
NOTE:This note is applicable only if you are using openam as a proxy. IGNORE THIS is you are using openam as IDP.
Login in to Openam Proxy >Go to Federation > Entity Providers (http://abc.com:8080/TST) -- Basically the remote Service provider )> SP> Advanced > IDP Proxy and make sure proxy count is set to 1 (i.e number one) and proxy url is set to your IDP url (yes the proxy url is idp url).The proxy uses this value to send request to IDP.
STEP11:
Verify that the acs url you have added in sp.xml is present in openam.
Login to IDP ( if you are using proxy login into openam proxy)> Click on Federation tab
Go to Entity providers section and click on the URL that we used to generate Fedlet (In this case we used http://abc.com:8080/TST )
While on SP tab go to “Services” screen scroll down to the section which has“Assertion consumer Service”
From: http://abc.com:8080/TST/fedletapplication
To: http://abc.com:8080/TST/saml/SSO/alias/realm001/sp
NOTE: I did not use single logout service. In case you want change the logout URL’s on this page. (Locally I changed all logout urls to “TST/saml/SingleLogout/alias/realm001/sp”)
Congratulations !!!! Your configuration to plugin SP with openam is complete.
We now need to focus on Spring code changes on you JSF web app.
or spring security saml tutorial
or spring security saml tutorial
or spring security jsf example
Here I will brief you about how to plugin spring code with your JSF application.
Please download the sample web application provided by
Vladmir Schaufer and explore a bit
You should be able to download
"spring-security-saml-1.0.0.RC2-dist.zip" or which ever is the most
recent one. Use maven and do a build and you will get a deploy-able war file
To run this example please read the java doc provided by
the author
Now in the zip file
spring-security-saml-1.0.0.RC2\sample\src\main\resources\security\securityContext.xml
Let’s use this sample spring security xml file as a
starting point for integrating our application.
If you read the documentation provided by Vladmir
Schaufer you will now be familiar with the Spring security xml. However to make
it work for our web app I really had to comment quite a few things.
So here is the Original Spring Security xml file
(original-securityContext.xml)
Here is the modified file (rams-applicationContext-security.xml).This file has comments in
each section indicating why I commented a xml entry. You can download the
modified xml file from below url
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
//We dont need metadata to be displayed comment this.
<!-- <security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/> -->
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
//We dont need Springdiscovery as we have one idp.So commented it.
<!-- <security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/> -->
This is how the spring filters will intercepts the urls.
Don't mess with this patterns.Thats the reason we changed the ACS and logout urls as mentioned in STEP5.
Once you have this rams-applicationContext-security.xml all it needs is the below files in your path or classpath. (See the rams-applicationContext-security.xml)
- sp.xml
- idp.xml
- java keystore with name keystore.jks (I copied from openam for testing)- I will add one article on keystores later.
- Your openam IDP URL (I used url as http://idp.abc.com:8080/openam
Ok now if you see my modified spring xml the only code you
need to write is the class com.tst.web.security.SAMLUserDetailsServiceImpl.java
which
is implementation for org.springframework.security.saml.userdetails.SAMLUserDetailsService
I will add the sample code here. I had integrated openam with Active directory and had configured openam to retrun the following attributes.
"ActiveDirGroups" and "UserName"
So when Openam sent the SAML Response it was sending these attributes. So we need to parse and build UserDetails object as below.
You can download the below code from
https://sites.google.com/site/reddymails/Home/SAMLUserDetailsServiceImpl.java?attredirects=0&d=1
package com.tst.web.security;package com.tst.web.security; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.xml.DOMConfigurator; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.saml.SAMLCredential; import org.springframework.security.saml.userdetails.SAMLUserDetailsService; /** * @author twreddy The SAMLUserDetailsService interface is similar to * UserDetailsService with difference that SAML data is used in order * obtain information about the user. So inspect the SAMLCredential * object and return such a data in a form of application specific * UserDetails object */ public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService { // SAML Response PAY load attributes. // Some of the attributes we get back in SAML RESPONSE // We mapped this in openam so it should be there in SAML response. public static final String GROUP_MEMBER_ATTR_NAME = "ActiveDirGroups"; public static final String USER_ID = "UserName"; private static Logger logger = LogManager.getLogger(FMSAMLUserDetailsService.class); /* * This is the method spring will invoke. */ public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException { logger = getLOSLogger(this); // Want to see if this object is null ... logger.info("credential=" + credential); if (credential != null) { String samlCredentilAsStr = ""; if (logger.isDebugEnabled()) { // I dont want read this object every time. // Will do only of we have enabled debugging. samlCredentilAsStr = ToStringBuilder.reflectionToString(credential); logger.debug("samlCredentilAsStr=" + samlCredentilAsStr); } String userId = getUserId(credential); logger.debug("userId=" + userId); UserDetails userDetails = getUserWithRoles(credential, userId); return userDetails; } throw new UsernameNotFoundException( "SAMLCredential is null. Cant extract " + GROUP_MEMBER_ATTR_NAME + " and or " + USER_ID + " in Saml Response. " ); } /** * Extracts all groups and creates a UserDetail object * and returns it. * @param credential * @param userId * @return */ private UserDetails getUserWithRoles(SAMLCredential credential, String userId) { UserDetails userDetails = null; // We dont have access to password ?? // So using name as password for now. List<SimpleGrantedAuthority> grantedRolesList = getGrantedAuthorities( GROUP_MEMBER_ATTR_NAME, credential); logger.info(" roles For :" + userId + " grantedRolesList=" + grantedRolesList); // This object needs userid and password. I dont have password // Faking it with user id. Password may really be needed when // you try to Authenticate this object using authManager. userDetails = new User(userId, userId, grantedRolesList); return userDetails; } /** * Reads the Attributes that are in SAML Pay load response. Our Group Meber * ship Is a long String: * CN=FILE_EDIT_USER,OU=Devtest,DC= We need to get only Group name which FILE_EDIT_USER * * @param name * @param credential * @return */ private List<SimpleGrantedAuthority> getGrantedAuthorities( final String name, final SAMLCredential credential) { List<SimpleGrantedAuthority> grantedRolesList = new ArrayList<SimpleGrantedAuthority>(); List<String> attrValueList = getAttributeValue(name,credential); for (String attrValue : attrValueList) { if (attrValue != null) { // All we are doing here is extracting the first part in the // group membership // eg: // CN=FILE_EDIT_USER,OU=Devtest,DC= We need to get only Group name which FILE_EDIT_USER logger.debug(" Raw Attribute=" + attrValue); String roleName = attrValue.split(",")[0].split("=")[1]; // We need just "FILE_EDIT_USER" logger.debug(" Cleaned up group name=" + roleName); logger.debug(" group name with case changed=" + roleName); SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority( roleName); grantedRolesList.add(grantedAuthority); } else { logger.warn("One of the Group attrValue was null."); } } return grantedRolesList; } /** * Given SAMLCredential inspects the object and returns the values * for give attribute name. * @param name * @param credential * @return */ public static List<String> getAttributeValue(final String name, final SAMLCredential credential) { List<String> attrValList = new ArrayList<String>(); Attribute attribute = credential.getAttributeByName(name); logger.debug(" Parsing name=" + name + " in SAMLCredential"); if (attribute != null) { List<XMLObject> attributes = attribute.getAttributeValues(); if ((attributes != null) && (attributes.size() > 0)) { for (XMLObject object : attributes) { XSString attrb = (XSString) object; String attrValue = attrb.getValue(); if (attrValue != null) { // clean unwanted strings here in the role logger.debug("name=" + name + " attrValue=" + attrValue); attrValList.add(attrValue); } } } } return attrValList; } /** * Reads the uid from SAML credential if present * @param credential * @return */ public static String getUserId(SAMLCredential credential) { String userId = null; List<String> userIdValueList = getAttributeValue(USER_ID, credential); logger.debug(" USER_ID="+ USER_ID +" userIdValueList=" + userIdValueList); if (userIdValueList.size() > 0) { userId = userIdValueList.get(0); } return userId; } }
So now you have spring security xml and the code to parse SAML repsonse ready. Just plugin. The srping xml in to your web.xml. Since the code above parsed the roles the user is we need to use those roles to enable or disable feature sin JSF pages which I will explain shortly.
In your webxml
I have attached the sample web.xml
https://sites.google.com/site/reddymails/Home/web.xml?attredirects=0&d=1
<listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/rams-applicationContext-security.xml </param-value> </context-param> <!-- Spring Security --> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
How do to we read the User name from Spring security context ?
I did some reading and here is code that saves you a day :)
The assumption is that your are sending "Username" as one of the attributes in SAML response.
Once you parse store it in Httpsession that way you don't run this code multiple times for same user.
public static final String USER_ID = "UserName";
public String getCurentUserName(){
// Read from Security context as SAML Pay loads gets your name. Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); SAMLCredential samlCredential = (SAMLCredential) authentication.getCredentials(); String userName = getUserId(samlCredential); return userName; } /** * Reads the uid from SAML credential if present * @param credential * @return */ public static String getUserId(SAMLCredential credential) { String userId = null; List<String> userIdValueList = getAttributeValue(USER_ID, credential); logger.debug(" USER_ID="+ USER_ID +" userIdValueList=" + userIdValueList); if (userIdValueList.size() > 0) { userId = userIdValueList.get(0); } return userId; }Once you have all this code you can plugin spring role based security if you need.With this you are done with single sign on. Howevere If you want to use Spring Security you can read th below few lines
Just follow these steps
<!-- To add Security tag tld to JSF you can read more at.
more at
http://static.springsource.org/spring-webflow/docs/2.2.x/reference/html/ch13s09.html
http://doanduyhai.wordpress.com/2012/02/26/spring-security-part-v-security-tags/
-->
Assuuming you have configured the Spring security tld now you can add name space in JSF page as shown below.
xmlns:security="http://www.springframework.org/security/tags"
And inside your JSF page now you can use tags like
rendered="#{security:areAnyGranted('FILE_READ_GROUP,FILE_EDIT_GROUP')}">
rendered="#security:areAllGranted('FILE_ALL_OPERATIONS_GROUP)"
disabled="#{security:areNotGranted('FILE_READ_GROUP)}">
Where FILE_READ_GROUP, FILE_ALL_OPERATIONS are Active directory groups the current user is in.
Hope this article take some pain of integrating openam with Spring SAML and also helps you in Spring Security integration with JSF application.
NOTE:If your web application is .net based you can try using .net fedelt
provided by openam.I don't have much knowledge to comment on .net.
If you like my articles or have some suggestion feel free to drop a message.