Yale CAS SSO + Alfresco 2.9.0b + Tomcat 5.5 的整合步骤

Alfresco,这个强大的开源内容管理项目,功能不用说了,关于其配置可参考官方的wiki资源和论坛,很详细!!!这里主要是结合Yale CAS做一个总结整理。

1.Alfresco与SSO:

     官方说明,Alfresco针对SSO具有良好的扩展性,主要体现在使用NTLM SSO认证,这个设置我做了一个星期都没有设置成功。与CAS SSO集成还算顺利,这里主要参考官方一个追踪单元:http://issues.alfresco.com/browse/AWC-952,其中工程师Andy做了解释:
Andy Hind - 28-Jun-07 08:14 PM A configurable filter (HTTPRequestAuthenticationFilter) has been added that should support this and other cases.
 
BTW

at org.alfresco.repo.security.authentication.AuthenticationComponentImpl.getUserDetails(AuthenticationComponentImpl.java:98)

Shows the alfresco authentication component is still configured.
You should use the example deny configuration in the config using SimpleAcceptOrRejectAllAuthenticationComponentImpl.
This supports setting the authenticated user without invoking the alfresco specific DAO for users which will not know about your users - but will check they exist. The filter is not doing this check in a user transaction. In previous versions there would have been auto transaction wrapping.

Thanks for providing the config settings for CAS

通过这个用例,我做了测试,在Alfresco中结合SSO,必须使用 SimpleAcceptOrRejectAllAuthenticationComponentImpl 作为AuthenticationComponent(认证组件),于是就可以实现了。

2.安装Yale CAS Server端

这里不用总结了,专门的Yale CAS Server总结另外我再写。

3.下载AlfrescoCommunity 2.9.0b

根据Use this defintion for Novell IChain integration,在authentication-services-context.xml中,使用Simple Authentication component如下:
< bean  id ="authenticationComponent"  class ="org.alfresco.repo.security.authentication.SimpleAcceptOrRejectAllAuthenticationComponentImpl" >
        
< property  name ="accept" >
            
< value > true </ value >
        
</ property >
    
</ bean >

注释掉如下:

<!--
    <bean id="authenticationComponent" class="org.alfresco.repo.security.authentication.AuthenticationComponentImpl">
        <property name="authenticationDao">
            <ref bean="authenticationDao" />
        </property>
        <property name="authenticationManager">
            <ref bean="authenticationManager" />
        </property>
        <property name="allowGuestLogin">
            <value>true</value>
        </property>
    </bean>
    
-->

4.参照webclient中NovellIChainsHTTPRequestAuthenticationFilter.java

写一个CasAuthenticationFilter如下:
/*
 * Copyright (C) 2005-2007 Alfresco Software Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

 * As a special exception to the terms and conditions of version 2.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * and Open Source Software ("FLOSS") applications as described in Alfresco's
 * FLOSS exception.  You should have recieved a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * 
http://www.alfresco.com/legal/licensing
 
*/

package  org.alfresco.web.app.servlet;

import  java.io.IOException;
import  java.util.List;
import  java.util.Locale;

import  javax.servlet.Filter;
import  javax.servlet.FilterChain;
import  javax.servlet.FilterConfig;
import  javax.servlet.ServletContext;
import  javax.servlet.ServletException;
import  javax.servlet.ServletRequest;
import  javax.servlet.ServletResponse;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;
import  javax.servlet.http.HttpSession;
import  javax.transaction.UserTransaction;

import  org.alfresco.config.ConfigService;
import  org.alfresco.i18n.I18NUtil;
import  org.alfresco.model.ContentModel;
import  org.alfresco.repo.security.authentication.AuthenticationComponent;
import  org.alfresco.repo.security.authentication.AuthenticationException;
import  org.alfresco.service.ServiceRegistry;
import  org.alfresco.service.cmr.repository.NodeRef;
import  org.alfresco.service.cmr.repository.NodeService;
import  org.alfresco.service.cmr.security.AuthenticationService;
import  org.alfresco.service.cmr.security.PersonService;
import  org.alfresco.service.transaction.TransactionService;
import  org.alfresco.web.app.Application;
import  org.alfresco.web.bean.LoginBean;
import  org.alfresco.web.bean.repository.User;
import  org.alfresco.web.config.LanguagesConfigElement;
import  org.apache.commons.logging.Log;
import  org.apache.commons.logging.LogFactory;
import  org.springframework.web.context.WebApplicationContext;
import  org.springframework.web.context.support.WebApplicationContextUtils;
import  org.alfresco.web.bean.repository.Repository;

import  edu.yale.its.tp.cas.client.filter.CASFilter;

/**
 * Sample authentication for CAS.
 *
 * 
@author Andy Hind
 * 
@author Laurent Meunier <l.meunier@atolcd.com>
 
*/

public   class  CasAuthenticationFilter  extends  AbstractAuthenticationFilter
        
implements  Filter  {
    
private static final String LOCALE = "locale";
    
public static final String MESSAGE_BUNDLE = "alfresco.messages.webclient";
    
private static Log logger = LogFactory
            .getLog(CasAuthenticationFilter.
class);
    
private ServletContext context;
    
private String loginPage;
    
private AuthenticationComponent authComponent;
    
private AuthenticationService authService;
    
private TransactionService transactionService;
    
private PersonService personService;
    
private NodeService nodeService;
    
private List<String> m_languages;

    
public CasAuthenticationFilter() {
        
super();
    }


    
public void destroy() {
        
// Nothing to do
    }


    
/**
     * Run the filter
     *
     * 
@param sreq
     *            ServletRequest
     * 
@param sresp
     *            ServletResponse
     * 
@param chain
     *            FilterChain
     * 
@exception IOException
     * 
@exception ServletException
     
*/

    
public void doFilter(ServletRequest sreq, ServletResponse sresp,
            FilterChain chain) 
throws IOException, ServletException {
        
// Get the HTTP request/response/session
        HttpServletRequest req = (HttpServletRequest) sreq;
        HttpServletResponse resp 
= (HttpServletResponse) sresp;

        HttpSession httpSess 
= req.getSession(true);

        String userName 
= null;
        Object o 
= httpSess.getAttribute(CASFilter.CAS_FILTER_USER);
        
if (o == null{
            logger.error(
"CAS : CASFilter.CAS_FILTER_USER == NULL");
        }
 else {
            userName 
= o.toString();
        }


        
if (logger.isDebugEnabled()) {
            logger.debug(
"CAS : User = " + userName);
        }


        
// See if there is a user in the session and test if it matches
        User user = (User) httpSess
                .getAttribute(AuthenticationHelper.AUTHENTICATION_USER);

        
if (user != null{
            
try {
                
// Debug
                if (logger.isDebugEnabled())
                    logger.debug(
"CAS : User " + user.getUserName()
                            
+ " validate ticket");

                
if (user.getUserName().equals(userName)) {
                    authComponent.setCurrentUser(user.getUserName());
                    I18NUtil.setLocale(Application.getLanguage(httpSess));
                    chain.doFilter(sreq, sresp);
                    
return;
                }
 else {
                    
// No match
                    setAuthenticatedUser(req, httpSess, userName);
                }

            }
 catch (AuthenticationException ex) {
                
if (logger.isErrorEnabled())
                    logger.error(
"Failed to validate user "
                            
+ user.getUserName(), ex);
            }

        }


        setAuthenticatedUser(req, httpSess, userName);

        
// Redirect the login page as it is never seen as we always login by
        
// name
        if (req.getRequestURI().endsWith(getLoginPage()) == true{
            
if (logger.isDebugEnabled())
                logger.debug(
"Login page requested, chaining ...");

            resp.sendRedirect(req.getContextPath()
                    
+ "/faces/jsp/browse/browse.jsp");
            
return;
        }
 else {
            chain.doFilter(sreq, sresp);
            
return;
        }

    }


    
/**
     * Set the authenticated user.
     *
     * It does not check that the user exists at the moment.
     *
     * 
@param req
     * 
@param httpSess
     * 
@param userName
     
*/

    
private void setAuthenticatedUser(HttpServletRequest req,
            HttpSession httpSess, String userName) 
{
        
// Set the authentication
        authComponent.setCurrentUser(userName);

        
// Set up the user information
        UserTransaction tx = transactionService.getUserTransaction();
        NodeRef homeSpaceRef 
= null;
        User user;
        
try {
            tx.begin();
            user 
= new User(userName, authService.getCurrentTicket(),
                    personService.getPerson(userName));
            homeSpaceRef 
= (NodeRef) nodeService.getProperty(personService
                    .getPerson(userName), ContentModel.PROP_HOMEFOLDER);
            
if (homeSpaceRef == null{
                logger.warn(
"Home Folder is null for user '" + userName
                        
+ "', using company_home.");
                homeSpaceRef 
= (NodeRef) nodeService.getRootNode(Repository
                        .getStoreRef());
            }

            user.setHomeSpaceId(homeSpaceRef.getId());
            tx.commit();
        }
 catch (Throwable ex) {
            logger.error(ex);

            
try {
                tx.rollback();
            }
 catch (Exception ex2) {
                logger.error(
"Failed to rollback transaction", ex2);
            }


            
if (ex instanceof RuntimeException) {
                
throw (RuntimeException) ex;
            }
 else {
                
throw new RuntimeException("Failed to set authenticated user",
                        ex);
            }

        }


        
// Store the user
        httpSess.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user);
        httpSess.setAttribute(LoginBean.LOGIN_EXTERNAL_AUTH, Boolean.TRUE);

        
// Set the current locale from the Accept-Lanaguage header if available
        Locale userLocale = parseAcceptLanguageHeader(req, m_languages);

        
if (userLocale != null{
            httpSess.setAttribute(LOCALE, userLocale);
            httpSess.removeAttribute(MESSAGE_BUNDLE);
        }


        
// Set the locale using the session
        I18NUtil.setLocale(Application.getLanguage(httpSess));

    }


    
public void init(FilterConfig config) throws ServletException {
        
this.context = config.getServletContext();
        WebApplicationContext ctx 
= WebApplicationContextUtils
                .getRequiredWebApplicationContext(context);
        ServiceRegistry serviceRegistry 
= (ServiceRegistry) ctx
                .getBean(ServiceRegistry.SERVICE_REGISTRY);
        transactionService 
= serviceRegistry.getTransactionService();
        nodeService 
= serviceRegistry.getNodeService();

        authComponent 
= (AuthenticationComponent) ctx
                .getBean(
"authenticationComponent");
        authService 
= (AuthenticationService) ctx
                .getBean(
"authenticationService");
        personService 
= (PersonService) ctx.getBean("personService");

        
// Get a list of the available locales
        ConfigService configServiceService = (ConfigService) ctx
                .getBean(
"webClientConfigService");
        LanguagesConfigElement configElement 
= (LanguagesConfigElement) configServiceService
                .getConfig(
"Languages").getConfigElement(
                        LanguagesConfigElement.CONFIG_ELEMENT_ID);

        m_languages 
= configElement.getLanguages();
    }


    
/**
     * Return the login page address
     *
     * 
@return String
     
*/

    
private String getLoginPage() {
        
if (loginPage == null{
            loginPage 
= Application.getLoginPage(context);
        }


        
return loginPage;
    }

}


5.设置web.xml,加入cas filter如下:

<!--  CAS SSO integration  -->
  
< filter >
    
< filter-name > CAS Required </ filter-name >
    
< filter-class > edu.yale.its.tp.cas.client.filter.CASFilter </ filter-class >
< init-param >
< param-name > logout_url </ param-name >
< param-value > https://localhost:8883/cas-server/logout &lt; /param-value>
</ init-param >
<!-- init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serviceUrl</param-name>
<param-value>http://localhost:8082/...&lt;/param-value>
</init-param
-->
    
< init-param >
        
< param-name > edu.yale.its.tp.cas.client.filter.loginUrl </ param-name >
        
< param-value > https://localhost:8883/cas-server/login &lt; /param-value>
    
</ init-param >
    
< init-param >
        
< param-name > edu.yale.its.tp.cas.client.filter.validateUrl </ param-name >
        
< param-value > https://localhost:8883/cas-server/serviceValidate &lt; /param-value>
    
</ init-param >
    
< init-param >
        
< param-name > edu.yale.its.tp.cas.client.filter.serverName </ param-name >
        
< param-value > localhost:8082 </ param-value >
    
</ init-param >
  
</ filter >

< filter >
      
< filter-name > Authentication Filter </ filter-name >
      
<!-- filter-class>org.alfresco.web.app.servlet.AuthenticationFilter</filter-class -->
      
< filter-class > org.alfresco.web.app.servlet.CASAuthenticationFilter </ filter-class >
   
</ filter >
   
< filter >
      
< filter-name > WebDAV Authentication Filter </ filter-name >
      
< filter-class > org.alfresco.repo.webdav.auth.AuthenticationFilter </ filter-class >
   
</ filter >
   
< filter >
      
< filter-name > Admin Authentication Filter </ filter-name >
      
< filter-class > org.alfresco.web.app.servlet.CASAuthenticationFilter </ filter-class >
      
<!-- filter-class>org.alfresco.web.app.servlet.AdminAuthenticationFilter</filter-class -->
   
</ filter >

< filter-mapping >
      
< filter-name > CAS Required </ filter-name >
      
< url-pattern > /faces/* </ url-pattern >
   
</ filter-mapping >
   
< filter-mapping >
                  
< filter-name > CAS Required </ filter-name >
                  
< url-pattern > /template/* </ url-pattern >
    
</ filter-mapping >
     
< filter-mapping >
                  
< filter-name > CAS Required </ filter-name >
                  
< url-pattern > /download/* </ url-pattern >
      
</ filter-mapping >

< filter-mapping >
      
< filter-name > Authentication Filter </ filter-name >
      
< url-pattern > /faces/* </ url-pattern >
   
</ filter-mapping >

这里,如果不进行第三步,系统测试会出现如下异常:
org.alfresco.error.AlfrescoRuntimeException: Transaction must be active and synchronization is required
at org.alfresco.repo.transaction.AlfrescoTransactionSupport.registerSynchronizations(AlfrescoTransactionSupport.java:371)
at org.alfresco.repo.transaction.AlfrescoTransactionSupport.getSynchronization(AlfrescoTransactionSupport.java:356)
at org.alfresco.repo.transaction.AlfrescoTransactionSupport.bindDaoService(AlfrescoTransactionSupport.java:210)
at org.alfresco.repo.transaction.TransactionalDaoInterceptor.invoke(TransactionalDaoInterceptor.java:66)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
at $Proxy1.getNode(Unknown Source)
.........
这里主要是利用org.alfresco.web.app.servlet.CASAuthenticationFilter替换org.alfresco.web.app.servlet.AdminAuthenticationFilter,这是关键处理!

6.使用了SSO,建议最好将CIFS和FTP服务关闭,因为在Afresco与SSO集成后,Alfresco作为客户应用端就无需做密码认证了,我在设置CIFS时失败,FTP时可以成功,但这也是危险的,当然可以在CIFS和FTP另外也强制进行密码认证处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值