This article describes one way to use Acegi Security System for Spring for authentication and authorization in Liferay (version 4.1.2).
One nice feature of Acegi Security is that you can easily implement different authentication schemes. I've set up Acegi to use CAS, but you can also use form based login or something else.
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext-acegi-security-cas.xml</param-value> </context-param> <filter> <filter-name>Acegi Filter Chain Proxy</filter-name> <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class> <init-param> <param-name>targetClass</param-name> <param-value>org.acegisecurity.util.FilterChainProxy</param-value> </init-param> </filter> <filter-mapping> <filter-name>Acegi Filter Chain Proxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<!-- +++++++++ Enforce Security based on URLs +++++++++ --> <!-- Intercept access to specified URLs and enforce security --> <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager"> <ref local="authenticationManager" /> </property> <property name="accessDecisionManager"> <ref local="accessDecisionManager" /> </property> <property name="objectDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON \A/c/portal/login\Z=ROLE_AUTHENTICATED \A/c/portal/logout\Z=ROLE_AUTHENTICATED \A/c/portal/layout.*\Z=ROLE_AUTHENTICATED \A/group/.*\Z=ROLE_AUTHENTICATED </value> </property> </bean>
AcegiAutoLogin has following tasks:
The main method of AcegiAutoLogin looks like this:
public String[] login(HttpServletRequest req, HttpServletResponse res) throws AutoLoginException {
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
if ((auth == null) || (!auth.isAuthenticated()) || (auth.getName() == null)) {
// user is not authenticated via Acegi
// or no user id is provided
// normal Liferay login procedure will be executed
return null;
}
String companyId = req.getSession().getServletContext().getInitParameter("company_id");
try {
User user = UserLocalServiceUtil.getUserById(auth.getName());
return updateLiferayAccount(auth.getName(), user, companyId);
} catch (Exception e) {
// user account not found in Liferay
return updateLiferayAccount(auth.getName(), null, companyId);
}
}
You'll have to add your own implementation of updateLiferayAccount to create or update account data with information from your user data store. Useful objects to access user data in Liferay are
If you have an external user directory (e.g. LDAP) you might want to synchronize the data between the directory and Liferay.
AcegiAutoLogin can be extended to update Liferay's user data on every login. This is done within the call of updateLiferayAccount(auth.getName(), user, companyId) in the code sample above.
If a user ore administrator changes user data from within Liferay the LDAP directory must be updated (otherwise the data will be overridden the next time the user logs in).
I've solved this by adding a Spring AOP AfterReturningAdvice. Add the following snippet to ext/ext-ejb/classes/META-INF/ext-spring-professional.xml:
<!--
- This TransactionProxyFactoryBean overrides the entry in portal-spring-professional.xml.
- The difference lays in the post interceptor which propagates certain user data
- settings to the LDAP directory.
-->
<bean id="com.liferay.portal.service.spring.UserService.transaction"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true">
<property name="transactionManager">
<ref bean="liferayTransactionManager" />
</property>
<property name="target">
<ref bean="com.liferay.portal.service.spring.UserService.professional" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
<property name="postInterceptors">
<list>
<ref bean="de.abas.liferay.service.impl.UserServiceAfterReturningAdvice"/>
</list>
</property>
</bean>
UserServiceAfterReturningAdvice implements org.springframework.aop.AfterReturningAdvice. The main method looks like this:
/**
* {@inheritDoc}
*
* Triggers on the execution of UserService methods modifying user data also
* kept in the LDAP directory. The data is then modified in LDAP, too.
*
* @see org.springframework.aop.AfterReturningAdvice#afterReturning(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
*/
public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
if (logger.isDebugEnabled()) {
if (LDAP_SYNCHRONIZATION_ENABLED.booleanValue()) {
logger.debug("LDAP synchronization disabled in portal-ext.properties.");
} else {
logger.debug("Executing UserServiceAfterReturningAdvice.");
}
}
if (!LDAP_SYNCHRONIZATION_ENABLED.booleanValue()) {
return;
}
if (m.getName().equals("updatePassword")) {
updatePassword(args);
} else if (m.getName().equals("updateUser")) {
updateUser(args);
} else if (m.getName().equals("updatePortrait")) {
updatePortrait(args);
}
}
Next to Authentication you can take advantage of Acegi Security for authorization. I want go into details, but if you are using Spring Portlet MVC you might be interested in this thread.
0 Attachments | Average (0 Votes) ![]() ![]() ![]() ![]() |