Gracefully handle expired HttpSession in Spring We

2020-07-12 19:45发布

问题:

(From SpringSource forum.)

When the HttpSession has expired and the user re-submits a page in the flow, he/she is sent back to the beginning of the flow. All I want to add to this behavior is a message explaining why it occurred. "You were inactive, so you have been restarted..."

What's the easiest/best-practice way to do this?

回答1:

The default behavior, in FlowHandlerAdapter.defaultHandleException(), "attempts to start a new execution of the ended or expired flow".

It looks like a WebFlow way to handle this would be to provide a FlowHandler with a handleException() method that checks for an instanceof NoSuchFlowExecutionException, then do something like construct a redirect URL or place something on Session scope that can later be removed once utilized.

Due to the way WebFlow uses redirects, I don't think any other scopes would allow such a flag or message to be used later when the new flow's view renders.

However, simply detecting a new Session in an Interceptor or even a Filter would seem to be just as effective. Which is what I ended up doing in my previous investigation of this, as documented in the referenced forum thread. I was just hoping for something prettier.

Also, by the time the new flow begins, a new Session ID has already been created, so there's no way to initially detect this condition from within the flow.xml.

Sample filter logic:

if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
    log.info("Expired Session ID: " + request.getRequestedSessionId());
    response.sendRedirect("sessionExpired");
}
else {
    chain.doFilter(request, response);
}

Sample Interceptor:

public class SessionExpiredInterceptor extends HandlerInterceptorAdapter
{
    private String redirectLocation = "sessionExpired";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
        Object handler) throws Exception
    {
        if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid())
        {
            response.sendRedirect(redirectLocation);
            return false;
        }

        return true;
    }

    public String getRedirectLocation() {
        return redirectLocation;
    }

    public void setRedirectLocation(String redirectLocation) {
        this.redirectLocation = redirectLocation;
    }
}


回答2:

Step 1: FlowController has a default handlerAdapter. To customize session exceptions you are required to write your own custom handler adapter and register it with the flow controller bean as below:

<bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
.
.<property name="flowHandlerAdapter" ref="customFlowHandlerAdapter"/>

.
</bean>

<bean id="customFlowHandlerAdapter" class="gov.mo.courts.pbw.adapters.CustomFlowHandlerAdapter"
     p:flowExecutor-ref="flowExecutor"/>

Step 2: CustomFlowHandlerAdapter In this class override defaultHandleException method. this is the method that webflow invokes in case of exceptions and re-initializes session. please note, new session has already been created till this point. Only the exception type will tell you at this point that the previous session timed out.

public class PbwFlowHandlerAdapter extends FlowHandlerAdapter{
protected void defaultHandleException(String flowId, FlowException e,
            HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        if(e instanceof NoSuchFlowExecutionException){
            if(e.getCause() instanceof NoSuchConversationException){
                //"use newly created session object within request object to save your   customized message."
            }
        }
        super.defaultHandleException(flowId, e, request, response);
    }

The first view page of your app should be able to show this message.

<%
                        if (session.getAttribute(YOUR_CUSTOM_MSG_KEY) != null) {
                    %>
                    <p class="errormessage">
                    <%=session.getAttribute(YOUR_CUSTOM_MSG_KEY)%>
                    </p>
                    <%
                    //once the message has been shown, remove it from the session
                    //as a new session has already been started at this time
                        session.removeAttribute(YOUR_CUSTOM_MSG_KEY);
                            }
                    %>

Hope this helps.



回答3:

From the Jira request I had opened, WebFlow developer says,

Custom FlowHandler is the extension point for an expired session. You can redirect to the flow and append a query param, something like restarted=true, and then in the flow declare an <input name="restarted" type="boolean" value="flashScope.restarted" />.

I haven't been able to confirm, but wanted to share the information.