Spring JSF在注销和会话失效后面临ajax请求
在我的Spring Boot1.2.7/JSF2.2.12/PrimeFaces5.2/Tomcat 8应用程序中,我试图在执行了/注销的网站上的AJAX调用后实现重定向到登录页面 为此,我添加了JSF重定向策略:Spring JSF在注销和会话失效后面临ajax请求,spring,jsf,primefaces,spring-boot,tomcat8,Spring,Jsf,Primefaces,Spring Boot,Tomcat8,在我的Spring Boot1.2.7/JSF2.2.12/PrimeFaces5.2/Tomcat 8应用程序中,我试图在执行了/注销的网站上的AJAX调用后实现重定向到登录页面 为此,我添加了JSF重定向策略: /** * Inspired by <a href= * "http://stackoverflow.com/questions/10143539/jsf-2-spring-security-3-x-and-richfaces-4-redirect-to-login-page-
/**
* 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 {
final static Logger logger = LoggerFactory.getLogger(JsfRedirectStrategy.class);
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;
}
}
这是注销链接:
<div id="LogoutContainer" class="PFTopLinks floatRight boldFont">
<h:form rendered="#{not empty request.remoteUser}">
<h:graphicImage name="main/images/pfPush.svg" />
<h:outputLink value="${pageContext.request.contextPath}/logout">
<span class="PFDarkText">Logout</span>
</h:outputLink>
</h:form>
</div>
注销
问题:现在JsfRedirectStrategy.onInvalidSessionDetected
在AJAX JSF调用中从未调用,因为请求会话管理筛选器.doFilter()中的.isRequestedSessionIdValid()
总是返回真
。
注销后,我有一个org.apache.catalina.session.StandardSessionFacade
我的代码有什么问题?我已经用以下代码(基于此主题)重新实现了此方法:
我添加了AjaxTimeoutPhaseListener
phase监听器:
public class AjaxTimeoutPhaseListener implements PhaseListener {
private static final long serialVersionUID = 2639152532235352192L;
public static Logger logger = LoggerFactory.getLogger(AjaxTimeoutPhaseListener.class);
@Override
public void afterPhase(PhaseEvent ev) {
}
@Override
public void beforePhase(PhaseEvent ev) {
FacesContext fc = FacesUtils.getContext();
RequestContext rc = RequestContext.getCurrentInstance();
HttpServletResponse response = FacesUtils.getResponse();
HttpServletRequest request = FacesUtils.getRequest();
if (FacesUtils.getExternalContext().getUserPrincipal() == null) {
if (FacesUtils.getExternalContext().isResponseCommitted()) {
// redirect is not possible
return;
}
try {
if (((rc != null && rc.isAjaxRequest())
|| (fc != null && fc.getPartialViewContext().isPartialRequest()))
&& fc.getResponseWriter() == null && fc.getRenderKit() == null) {
response.setCharacterEncoding(request.getCharacterEncoding());
RenderKitFactory factory = (RenderKitFactory) FactoryFinder
.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit renderKit = factory.getRenderKit(fc,
fc.getApplication().getViewHandler().calculateRenderKitId(fc));
ResponseWriter responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
request.getCharacterEncoding());
responseWriter = new PartialResponseWriter(responseWriter);
fc.setResponseWriter(responseWriter);
FacesUtils.redirect("/login.xhtml");
}
} catch (IOException ex) {
StringBuilder error = new StringBuilder("Redirect to the specified page '");
error.append("/login.xhtml");
error.append("' failed");
logger.error(error.toString(), ex);
throw new FacesException(ex);
}
} else {
return; // This is not a timeout case . Do nothing !
}
}
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
还添加了FacesUtils
class(从lib中提取):
现在一切正常了我用以下代码重新实现了这种方法(基于此主题):
我添加了AjaxTimeoutPhaseListener
phase监听器:
public class AjaxTimeoutPhaseListener implements PhaseListener {
private static final long serialVersionUID = 2639152532235352192L;
public static Logger logger = LoggerFactory.getLogger(AjaxTimeoutPhaseListener.class);
@Override
public void afterPhase(PhaseEvent ev) {
}
@Override
public void beforePhase(PhaseEvent ev) {
FacesContext fc = FacesUtils.getContext();
RequestContext rc = RequestContext.getCurrentInstance();
HttpServletResponse response = FacesUtils.getResponse();
HttpServletRequest request = FacesUtils.getRequest();
if (FacesUtils.getExternalContext().getUserPrincipal() == null) {
if (FacesUtils.getExternalContext().isResponseCommitted()) {
// redirect is not possible
return;
}
try {
if (((rc != null && rc.isAjaxRequest())
|| (fc != null && fc.getPartialViewContext().isPartialRequest()))
&& fc.getResponseWriter() == null && fc.getRenderKit() == null) {
response.setCharacterEncoding(request.getCharacterEncoding());
RenderKitFactory factory = (RenderKitFactory) FactoryFinder
.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit renderKit = factory.getRenderKit(fc,
fc.getApplication().getViewHandler().calculateRenderKitId(fc));
ResponseWriter responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
request.getCharacterEncoding());
responseWriter = new PartialResponseWriter(responseWriter);
fc.setResponseWriter(responseWriter);
FacesUtils.redirect("/login.xhtml");
}
} catch (IOException ex) {
StringBuilder error = new StringBuilder("Redirect to the specified page '");
error.append("/login.xhtml");
error.append("' failed");
logger.error(error.toString(), ex);
throw new FacesException(ex);
}
} else {
return; // This is not a timeout case . Do nothing !
}
}
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
还添加了FacesUtils
class(从lib中提取):
现在一切正常
public class FacesUtils {
public static Logger logger = LoggerFactory.getLogger(FacesUtils.class);
/**
* Returns the current faces context.
* <p>
* <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
* submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
* task.</i>
* @return The current faces context.
* @see FacesContext#getCurrentInstance()
*/
public static FacesContext getContext() {
return FacesContext.getCurrentInstance();
}
/**
* Returns the HTTP servlet response.
* <p>
* <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
* submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
* task.</i>
* @return The HTTP servlet response.
* @see ExternalContext#getResponse()
*/
public static HttpServletResponse getResponse() {
return getResponse(getContext());
}
/**
* {@inheritDoc}
* @see Faces#getResponse()
*/
public static HttpServletResponse getResponse(FacesContext context) {
return (HttpServletResponse) context.getExternalContext().getResponse();
}
/**
* Returns the HTTP servlet request.
* <p>
* <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
* submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
* task.</i>
* @return The HTTP servlet request.
* @see ExternalContext#getRequest()
*/
public static HttpServletRequest getRequest() {
return getRequest(getContext());
}
/**
* {@inheritDoc}
* @see Faces#getRequest()
*/
public static HttpServletRequest getRequest(FacesContext context) {
return (HttpServletRequest) context.getExternalContext().getRequest();
}
/**
* Returns the current external context.
* <p>
* <i>Note that whenever you absolutely need this method to perform a general task, you might want to consider to
* submit a feature request to OmniFaces in order to add a new utility method which performs exactly this general
* task.</i>
* @return The current external context.
* @see FacesContext#getExternalContext()
*/
public static ExternalContext getExternalContext() {
return getContext().getExternalContext();
}
/**
* Returns the HTTP request context path. It's the webapp context name, with a leading slash. If the webapp runs
* on context root, then it returns an empty string.
* @return The HTTP request context path.
* @see ExternalContext#getRequestContextPath()
*/
public static String getRequestContextPath() {
return getRequestContextPath(getContext());
}
/**
* {@inheritDoc}
* @see Faces#getRequestContextPath()
*/
public static String getRequestContextPath(FacesContext context) {
return context.getExternalContext().getRequestContextPath();
}
/**
* Does a regular or ajax redirect.
*/
public static void redirect(String redirectPage) throws FacesException {
checkViewRoot(FacesUtils.getContext(), FacesUtils.getRequestContextPath());
FacesContext fc = FacesUtils.getContext();
ExternalContext ec = fc.getExternalContext();
try {
if (ec.isResponseCommitted()) {
// redirect is not possible
return;
}
// fix for renderer kit (Mojarra's and PrimeFaces's ajax redirect)
if ((RequestContext.getCurrentInstance().isAjaxRequest() || fc.getPartialViewContext().isPartialRequest())
&& fc.getResponseWriter() == null && fc.getRenderKit() == null) {
ServletResponse response = (ServletResponse) ec.getResponse();
ServletRequest request = (ServletRequest) ec.getRequest();
response.setCharacterEncoding(request.getCharacterEncoding());
RenderKitFactory factory = (RenderKitFactory) FactoryFinder
.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit renderKit = factory.getRenderKit(fc,
fc.getApplication().getViewHandler().calculateRenderKitId(fc));
ResponseWriter responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
request.getCharacterEncoding());
fc.setResponseWriter(responseWriter);
}
ec.redirect(ec.getRequestContextPath() + (redirectPage != null ? redirectPage : ""));
} catch (IOException e) {
logger.error("Redirect to the specified page '" + redirectPage + "' failed");
throw new FacesException(e);
}
}
public static void checkViewRoot(FacesContext ctx, String viewId) {
if (ctx.getViewRoot() == null) {
UIViewRoot viewRoot = ctx.getApplication().getViewHandler().createView(ctx, viewId);
if (viewRoot != null) {
ctx.setViewRoot(viewRoot);
}
}
}
}
<lifecycle>
<phase-listener>com.domain.AjaxTimeoutPhaseListener</phase-listener>
</lifecycle>