Skip to content

Instantly share code, notes, and snippets.

@banterCZ
Last active May 15, 2021 15:26
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save banterCZ/5160269 to your computer and use it in GitHub Desktop.
Save banterCZ/5160269 to your computer and use it in GitHub Desktop.
InvalidSessionStrategy when session expired and ajax request is done.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Inspired by <a href="http://stackoverflow.com/questions/10143539/jsf-2-spring-security-3-x-and-richfaces-4-redirect-to-login-page-on-session-tim">StackOverflow.com</a>
* and by <a href="http://www.icesoft.org/wiki/display/ICE/Spring+Security#SpringSecurity-Step4%3AConfigureYourSpringSecurityredirectStrategy">Spring Security 3 and ICEfaces 3 Tutorial</a>.
*
* @author banterCZ
*/
public class JsfRedirectStrategy implements InvalidSessionStrategy {
private Logger logger = LoggerFactory.getLogger(getClass());
private static final String FACES_REQUEST_HEADER = "faces-request";
private String invalidSessionUrl;
/**
* {@inheritDoc}
*/
@Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
boolean ajaxRedirect = "partial/ajax".equals(request.getHeader(FACES_REQUEST_HEADER));
if(ajaxRedirect) {
String contextPath = request.getContextPath();
String redirectUrl = contextPath + invalidSessionUrl;
logger.debug("Session expired due to ajax request, redirecting to '{}'", redirectUrl);
String ajaxRedirectXml = createAjaxRedirectXml(redirectUrl);
logger.debug("Ajax partial response to redirect: {}", ajaxRedirectXml);
response.setContentType("text/xml");
response.getWriter().write(ajaxRedirectXml);
} else {
String requestURI = getRequestUrl(request);
logger.debug("Session expired due to non-ajax request, starting a new session and redirect to requested url '{}'", requestURI);
request.getSession(true);
response.sendRedirect(requestURI);
}
}
private String getRequestUrl(HttpServletRequest request) {
StringBuffer requestURL = request.getRequestURL();
String queryString = request.getQueryString();
if (StringUtils.hasText(queryString)) {
requestURL.append("?").append(queryString);
}
return requestURL.toString();
}
private String createAjaxRedirectXml(String redirectUrl) {
return new StringBuilder()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.append("<partial-response><redirect url=\"")
.append(redirectUrl)
.append("\"></redirect></partial-response>")
.toString();
}
public void setInvalidSessionUrl(String invalidSessionUrl) {
this.invalidSessionUrl = invalidSessionUrl;
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<sec:http pattern="/pages/public/**" security="none"/>
<sec:http pattern="/javax.faces.resource/**" security="none"/>
<sec:http use-expressions="true" >
<sec:custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER" />
<sec:intercept-url pattern="/pages/protected/**" access="isAuthenticated()" />
<sec:form-login login-page='/pages/public/login.xhtml' default-target-url="/pages/protected/index.xhtml"
authentication-failure-url="/pages/public/login.xhtml?failure=true" />
<sec:logout logout-success-url="/pages/public/login.xhtml"/>
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="joe" password="password" authorities="ROLE_USER"/>
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
<bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
<property name="invalidSessionStrategy" ref="jsfRedirectStrategy" />
</bean>
<bean id="jsfRedirectStrategy" class="JsfRedirectStrategy">
<property name="invalidSessionUrl" value="/pages/public/error/viewExpired.xhtml" />
</bean>
<bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
</beans>
<ui:composition template="/WEB-INF/templates/public_layout.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<ui:param name="title" value="#{msg['error.viewExpired.title']}" />
<ui:define name="content">
#{msg['error.viewExpired.text']}
<h:outputLink value="#{header['referer']}">#{msg['link.back']}</h:outputLink>
</ui:define>
</ui:composition>
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
@ThatsAMorais
Copy link

My hero

@jittagornp
Copy link

thank you very much for solution :)

@luisffc
Copy link

luisffc commented May 21, 2014

Thank a lot man!

@marcelmcz
Copy link

Thank, you bro!
It helps me a lot. With your solution, i could turn my javascript's integrations more rich. I only add "response.setStatus(int n)" in onInvalidSessionDetected file to facilitate in jQuery.post.

:)

@edmarr
Copy link

edmarr commented Oct 2, 2014

You saved my life ;)

@mhewedy
Copy link

mhewedy commented Nov 20, 2014

Great work, however, you shouldn't put the RedirectStrategy code inside your implementation of "InvalidSessionStrategy", however you should delegate the work to an implementation of RedirectStrategy instead. (exactly as SimpleRedirectInvalidSessionStrategy works.)

@AbdelhamidUPOND
Copy link

Great work, however, I added in the file of config
and in the face-config
< lifecycle>

web.util.CacheControlPhaseListener< /phase-listener>
< / lifecycle>

public class CacheControlPhaseListener implements PhaseListener {
private static final long serialVersionUID = 2475683599960651934L;
@OverRide
public void afterPhase(PhaseEvent event) {}

@Override
public void beforePhase(PhaseEvent event) {
    FacesContext facesContext = event.getFacesContext();
    HttpServletResponse response = (HttpServletResponse) facesContext
            .getExternalContext().getResponse();
    response.addHeader("Pragma", "no-cache");
    response.addHeader("Cache-Control", "no-cache");
    // Stronger according to blog comment below that references HTTP spec
    response.addHeader("Cache-Control", "no-store");
    response.addHeader("Cache-Control", "must-revalidate");
    // some date in the past
    response.addHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");

}

@Override
public PhaseId getPhaseId() {
         return PhaseId.RENDER_RESPONSE;
}

}

otherwise many problems appear.
what do you think ?

@rogerio-ar-costa
Copy link

Worked like a charm, thank you!

@agustisanchez
Copy link

Thanks!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment