Alfresco,这个强大的开源内容管理项目,功能不用说了,关于其配置可参考官方的wiki资源和论坛,很详细!!!这里主要是结合Yale CAS做一个总结整理。
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(认证组件),于是就可以实现了。
<
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>
-->
/*
* 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;
}
}
<!--
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
<
/param-value>
</
init-param
>
<!--
init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serviceUrl</param-name>
<param-value>http://localhost:8082/...</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
<
/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
<
/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另外也强制进行密码认证处理。
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如下:





注释掉如下:













4.参照webclient中NovellIChainsHTTPRequestAuthenticationFilter.java
写一个CasAuthenticationFilter如下:


















































































































































































































































































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




























































这里,如果不进行第三步,系统测试会出现如下异常:
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另外也强制进行密码认证处理。