Integration with Siteminder SSO

Introduction #

This document is out dated and preceded by CA SiteMinder Integration. Versions older than 5.1.2 can still benefit from this document as this describes how the integration can be achieved in Liferay Extension Environment.

The purpose of this paper is to explain how to integrate Siteminder Single Sign-On (SSO) into the Liferay Portal and to discuss some related issues dealing with that integration. By default, the portal uses its own authorization, i.e. user name and password, to identify a user, but that behavior can be overridden in a number of ways. Liferay supports some external authorization methods like Lightweight Directory Access Protocol (LDAP) to any compliant LDAP database as well as a Central Authorization Service (JA-SIG CAS), NT LAN Manager (NTLM), OpenID, and OpenSSO. Liferay added native support for external authorization with Computer Associate’s (CA) Siteminder starting in Liferay Portal 5.1.2 SE and it is now available in all releases of Liferay Portal 5.1 EE and Liferay Portal 5.2 SE / EE. Prior to 5.1.2, support can be accomplished by using the extension environment and making some changes in the portal configuration files.

Siteminder SSO Execution#

Once the Siteminder agent authorizes a user, it passes information through the HTTP header that can be used by Liferay to log this user into the portal. The portal developer needs to map an attribute, that Siteminder is sending in the header, to a Liferay attribute. In our case, we decided to map the Siteminder distinguished user name (SM_USERDN) to the Liferay screen name. The following paragraphs describe the steps we took to achieve this integration:

Extended Environment#

We created a com.ext.security.auth.SiteminderAutoLogin.java class in the ext-impl/src directories of the extended environment. This Java class would process the Siteminder token in the HTTP header. My SiteminderAutoLogin class is taking that token and logging the user into Liferay. I have attached some of that code to show the basic steps. The first statement captures the Siteminder token:

String aUsername = req.getHeader("SM_USERDN");
The code uses the Siteminder user name to find if a user with such a screen name exists and, if that user exists, gets it’s password.
	Company company = PortalUtil.getCompany(req);
User user = UserLocalServiceUtil.getUserByScreenName(company.getCompanyId(), akoUsername); String password = user.getPassword(); }}} Then, the user is automatically logged in.

Portal Configuration#

The portal needs to be informed that, before the standard login process in triggered, there is an alternative method that needs to be executed first. This is done in the portal-ext.properties file. We defined an auto login hook for Siteminder that tells Liferay to execute our SiteminderAutoLogin class when the portal page is requested and the requester is not logged in. The setting is:

auto.login.hooks=com.ext.security.auth.SiteminderAutoLogin

Other Considerations#

The basis for the system’s authorization is that Siteminder will send the portal a valid user name, or screen name. We therefore added the following configuration to the portal-ext.properties file to specify that the portal will authorize via screen name:

company.security.auth.type=screenName
Since authorization to the portal is being done exclusively through Siteminder SSO, we did not want users to know they have portal passwords. When a user is created the password is automatically generated i.e. the password is a random set of alpha-numeric characters, and that password is not saved. We disable the user from asking that their password be sent to them via email by this configuration setting:
company.security.send.password=false
Since accounts have to be created so that the Siteminder token will match the screen name, we disable guest users from creating accounts. Account creation needs to be coordinated with the enterprise group. To disable guest users from creating accounts we put this configuration setting into the portal-ext.properties file:
company.security.strangers=false
Additionally, a user who is not an administrator must not be able to change their screen name. We needed to change the edit user profile code to allow only the portal administrator role to change screen names. We replaced the ext-web/docroot/html/portlet/enterprise_admin/edit_user_profile.jspf file with extended code that determined whether the user (user2) that was requesting the user modification had the Administrator role. If that user had this role, the screen name field would be an enabled text box. Otherwise, the form would display the screen name as text and create a hidden variable with the same value that would be sent to the action class. This change needs to be done for both the Enterprise Admin and MyAccount portlets.

Faking out Liferay#

Due to cost and other considerations, many systems that are equipped with a Siteminder agent in their production environment have development and staging environments that are not equipped with this agent. To run the portal as if it were under Siteminder authentication it needs the Siteminder token to log the user in. There needs to be a way to test out the extended Siteminder functionality of the Liferay portal in environments that do not have the Siteminder agent. To do this, we created a java class that implements javax.servlet.Filter which adds a SM_USERDN attribute with the value specified by the param-value to the HTTP header. We called this class com.company.sso.SiteMinderHeaderSpoofFilter and we added the reference to this filter to the ext-web/docroot/WEF-INF/web.xml file. The reference looks like this:

  <filter> 
<filter-name>SiteMinderHeaderSpoofFilter</filter-name> <filter-class>com.company.sso.SiteMinderHeaderSpoofFilter</filter-class> <init-param> <param-name>SM_USERDN</param-name> <param-value>my.screen.name</param-value> </init-param> </filter> <filter-mapping> <filter-name>SiteMinderHeaderSpoofFilter</filter-name> <url-pattern>/</url-pattern> </filter-mapping>}}} When the extended environment was rebuilt, the web.xml in the application server directory should have this filter. The addition to the web.xml file tells the portal to execute the SiteMinderHeaderSpoofFilter filter whenever for all URLs that match any URL in the application, i.e. /. The SiteMinderHeaderSpoofFilter code looks for the SM_USERDN initialization parameter and uses that value to create a filter in the HTTP request. The filter is called SiteMinderSpoofRequest. The following box shows SiteMinderHeaderSpoofFilter code:

public class SiteMinderHeaderSpoofFilter implements Filter  {

    private FilterConfig filterConfig = null;
    private String smUserDN = null;

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;

        smUserDN = filterConfig.getInitParameter("SM_USERDN");
    }

    public void destroy() {
        this.filterConfig = null;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
         throws IOException, ServletException {
    
        if (request instanceof HttpServletRequest) {
            HttpServletRequest spoofRequest = new SiteMinderSpoofRequestWrapper((HttpServletRequest)request, smUserDN);
            chain.doFilter(spoofRequest, response);
        } else {
            // this isn't an HTTP request
            throw new RuntimeException("This web application only accepts HTTP requests.");
        }
    }
}

The SiteMinderSpoofRequestWrapper code simply returns the value of SM_USERDN. Its code looks like this:

public class SiteMinderSpoofRequestWrapper extends HttpServletRequestWrapper {
    
        private String smUserDN;

        public SiteMinderSpoofRequestWrapper(HttpServletRequest httpServletRequest, String smUserDN) {
            super(httpServletRequest);
            this.smUserDN = smUserDN;
        }

        public String getHeader(String name) {
            if ("SM_USERDN".equalsIgnoreCase(name)) {
                return smUserDN;
            } else {
                return super.getHeader(name);
            }
        }
        
        public Enumeration getHeaders(String name) {
            Vector headers = new Vector();
            if ("SM_USERDN".equalsIgnoreCase(name)) {
                headers.add(smUserDN);
                return headers.elements();
            } else {
                return super.getHeaders(name);
            }
        }
        
        public Enumeration getHeaderNames() {
            Vector headerNames = new Vector();
            Enumeration enumNames = super.getHeaderNames();
            while (enumNames.hasMoreElements()) {
                headerNames.add( (String)enumNames.nextElement() );
            }
            if (!headerNames.contains("SM_USERDN")) {
                headerNames.add("SM_USERDN");
            }
            return headerNames.elements();
        }

    }
}
0 附件
47966 查看
平均 (1 投票)
满分为 5,平均得分为 3.0。
评论
讨论主题回复 作者 日期
Simple and straight forward documentation about... krunal b soni 2010年3月13日 上午5:25
>> Faking out Liferay If you like Firefox, get... Sudhaker Raj 2010年7月27日 下午1:35

Simple and straight forward documentation about CA Siteminder Integration with Older version of Liferay where SiteMinder is not the default one

Thanks
在 10-3-13 上午5:25 发帖。
>> Faking out Liferay

If you like Firefox, get following extension and start faking liferay without any hardcoding ;-) I just add my custom SM_USER header and proceed with the testing...

https://addons.mozilla.org/en-US/firefox/addon/967/

Hope this helps someone.
在 10-7-27 下午1:35 发帖。