Spring JSF在注销和会话失效后面临ajax请求

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-

在我的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-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>