Hi,
I'm developing richfaces application with webflow 2.0.7, richfaces 3.3.1 and spring security 2.0.5.
I have a form with selectOneMenu that makes an ajax request on change and rerenders another selectOneMenu.
The problem is that if I make the ajax requiest after the session timeout, I'm redirected to the login page, but after that I recieve strange XML :
After some investigation I found the problem and the solution.Code:<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"><head><meta name="Ajax-Response" content="redirect" /><meta name="Location" content="/Schoolix/groups/new.sp" /></head></html>
The problem is in the BaseXMLFilter in richfaces and specially in this code
As it is written in the comment "Special handling of redirect - client-side script must // Check for response and perform redirect by window.location" we need to redirect manually, but since our XMLHTTPRequest callback is no longer available we cannot do anything from the client.Code:if (null != redirectLocation) { if (isAjaxRequest(request)) { // Special handling of redirect - client-side script must // Check for response and perform redirect by window.location if (log.isDebugEnabled()) { log.debug("Create AJAX redirect response to url: " + redirectLocation); } output = resetResponse(response, servletResponseWrapper, "redirect"); response.setHeader(AjaxContainerRenderer.AJAX_LOCATION_HEADER, redirectLocation); // For buggy XmlHttpRequest realisations repeat headers in // <meta> output.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>" + "<meta name=\"" + AjaxContainerRenderer.AJAX_FLAG_HEADER + "\" content=\"redirect\" />" + "<meta name=\"" + AjaxContainerRenderer.AJAX_LOCATION_HEADER + "\" content=\"" + redirectLocation + "\" />" + "</head></html>"); output.flush(); response.flushBuffer(); } else { response.sendRedirect(redirectLocation); }
So we need normal redirect, not the ajax one. This is determined with the following function in BaseXMLFilterI traced the request and this is the request stored by the ExceptionTranslationFilter#sendStartAuthentication :Code:protected boolean isAjaxRequest(ServletRequest request) { try { return null != request .getParameter(AjaxContainerRenderer.AJAX_PARAMETER_NAME); } catch (Exception e) { // OCJ 10 - throw exception for static resources. return false; } }
So my solution is to remove the parameter from the request if the session is timed out:Code:protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException { HttpServletRequest httpRequest = (HttpServletRequest) request; SavedRequest savedRequest = new SavedRequest(httpRequest, portResolver); if (logger.isDebugEnabled()) { logger.debug("Authentication entry point being called; SavedRequest added to Session: " + savedRequest); } if (createSessionAllowed) { // Store the HTTP request itself. Used by AbstractProcessingFilter // for redirection after successful authentication (SEC-29) httpRequest.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest); } // SEC-112: Clear the SecurityContextHolder's Authentication, as the // existing Authentication is no longer considered valid SecurityContextHolder.getContext().setAuthentication(null); authenticationEntryPoint.commence(httpRequest, response, reason); }
and the custom entry point configurationCode:package eu.dreamix.schoolix.view.security; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.ajax4jsf.renderkit.AjaxContainerRenderer; import org.springframework.security.AuthenticationException; import org.springframework.security.ui.AbstractProcessingFilter; import org.springframework.security.ui.savedrequest.SavedRequest; import org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint; import org.springframework.security.util.PortResolverImpl; /** * @author Cvetelin Andreev Date: 26.08.2009 */ public class FixAjaxRedirectEntryPoint extends AuthenticationProcessingFilterEntryPoint { @SuppressWarnings("serial") @Override public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException) throws IOException, ServletException { super.commence(request, response, authException); if (request instanceof HttpServletRequest) { if (true == isAjaxRequest(request)) { HttpServletRequest httpRequest = (HttpServletRequest) request; SavedRequest savedRequest = new SavedRequest(httpRequest, new PortResolverImpl()) { @Override public String[] getParameterValues(String name) { if (AjaxContainerRenderer.AJAX_PARAMETER_NAME == name) { return null; } return super.getParameterValues(name); } }; httpRequest.getSession().setAttribute( AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest); } } } protected boolean isAjaxRequest(ServletRequest request) { try { return null != request .getParameter(AjaxContainerRenderer.AJAX_PARAMETER_NAME); } catch (Exception e) { return false; } } }
Code:<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd"> <http auto-config='true' access-denied-page="/login.sp" entry-point-ref="fixAjaxRedirectEntryPoint"> <intercept-url pattern="/css/**" filters="none" /> <intercept-url pattern="/a4j/**" filters="none" /> <intercept-url pattern="/login.sp**" filters="none" /> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login login-page="/login.sp" authentication-failure-url="/login.sp?login_error=1" /> <logout logout-success-url="/login.sp" /> </http> <authentication-provider> <user-service> <user name="cvetelin.andreev@dreamix.eu" password="" authorities="ROLE_USER" /> <user name="mimi.maneva@gmail.com" password="dupe" authorities="ROLE_USER" /> </user-service> </authentication-provider> <beans:bean id="fixAjaxRedirectEntryPoint" class="eu.dreamix.schoolix.view.security.FixAjaxRedirectEntryPoint"> <beans:property name="loginFormUrl" value="/login.sp"></beans:property> </beans:bean> </beans:beans>


